diff --git a/.travis.yml b/.travis.yml index 78b3c56..f70e535 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,8 @@ language: objective-c -osx_image: xcode9 xcode_project: Franz.xcodeproj -xcode_scheme: franz_Package - -branches: - only: - - swiftPackageManager +xcode_scheme: franz-Package +osx_image: xcode9 +sudo: required +services: + - docker +script: xcodebuild test -project Franz.xcodeproj -scheme Franz diff --git a/Cocoapods Example/.gitignore b/Cocoapods Example/.gitignore new file mode 100644 index 0000000..8f30a7e --- /dev/null +++ b/Cocoapods Example/.gitignore @@ -0,0 +1 @@ +Pods diff --git a/Example/Franz.xcodeproj/project.pbxproj b/Cocoapods Example/Franz.xcodeproj/project.pbxproj similarity index 61% rename from Example/Franz.xcodeproj/project.pbxproj rename to Cocoapods Example/Franz.xcodeproj/project.pbxproj index 283b74c..535e3bd 100644 --- a/Example/Franz.xcodeproj/project.pbxproj +++ b/Cocoapods Example/Franz.xcodeproj/project.pbxproj @@ -7,31 +7,17 @@ objects = { /* Begin PBXBuildFile section */ + 3016416A56168C8638103E14 /* Pods_Franz_Example.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6C3323F089BC766914CED93D /* Pods_Franz_Example.framework */; }; 607FACD61AFB9204008FA782 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACD51AFB9204008FA782 /* AppDelegate.swift */; }; 607FACDB1AFB9204008FA782 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 607FACD91AFB9204008FA782 /* Main.storyboard */; }; 607FACDD1AFB9204008FA782 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 607FACDC1AFB9204008FA782 /* Images.xcassets */; }; 607FACE01AFB9204008FA782 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 607FACDE1AFB9204008FA782 /* LaunchScreen.xib */; }; - 607FACEC1AFB9204008FA782 /* Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACEB1AFB9204008FA782 /* Tests.swift */; }; - 76299E1A1C5587AF00C4EDE1 /* Pods_Franz_Example.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 76299E191C5587AF00C4EDE1 /* Pods_Franz_Example.framework */; }; 76299E1D1C55894600C4EDE1 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACD71AFB9204008FA782 /* ViewController.swift */; }; - 9EC11FF172797A9FE41EB395 /* Pods_Franz_Tests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7E5F9C962BD5A14B1C4CA01D /* Pods_Franz_Tests.framework */; }; /* End PBXBuildFile section */ -/* Begin PBXContainerItemProxy section */ - 607FACE61AFB9204008FA782 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 607FACC81AFB9204008FA782 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 607FACCF1AFB9204008FA782; - remoteInfo = Franz; - }; -/* End PBXContainerItemProxy section */ - /* Begin PBXFileReference section */ 1FB3D3E1489F609397269983 /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = LICENSE; path = ../LICENSE; sourceTree = ""; }; 3F1786530256FB4A58B4F4D8 /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = ""; }; - 4A93C2D572B5914E568B1536 /* Pods-Franz_Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Franz_Example.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Franz_Example/Pods-Franz_Example.debug.xcconfig"; sourceTree = ""; }; - 5AEC21E89DB8D5C0680DC1CB /* Pods-Franz_Tests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Franz_Tests.release.xcconfig"; path = "Pods/Target Support Files/Pods-Franz_Tests/Pods-Franz_Tests.release.xcconfig"; sourceTree = ""; }; 607FACD01AFB9204008FA782 /* Franz_Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Franz_Example.app; sourceTree = BUILT_PRODUCTS_DIR; }; 607FACD41AFB9204008FA782 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 607FACD51AFB9204008FA782 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; @@ -39,15 +25,13 @@ 607FACDA1AFB9204008FA782 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 607FACDC1AFB9204008FA782 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 607FACDF1AFB9204008FA782 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; - 607FACE51AFB9204008FA782 /* Franz_Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Franz_Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 607FACEA1AFB9204008FA782 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 607FACEB1AFB9204008FA782 /* Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tests.swift; sourceTree = ""; }; - 75D0B0B27AA0D734FC02B7E0 /* Pods_Franz_Example.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Franz_Example.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 76299E191C5587AF00C4EDE1 /* Pods_Franz_Example.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Pods_Franz_Example.framework; path = "Pods/../build/Debug-iphoneos/Pods_Franz_Example.framework"; sourceTree = ""; }; - 7E5F9C962BD5A14B1C4CA01D /* Pods_Franz_Tests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Franz_Tests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - BDE1F5B5CEEF3744FE48D035 /* Pods-Franz_Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Franz_Example.release.xcconfig"; path = "Pods/Target Support Files/Pods-Franz_Example/Pods-Franz_Example.release.xcconfig"; sourceTree = ""; }; - C3F5A52FA1C23AA2BC138DBD /* Pods-Franz_Tests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Franz_Tests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Franz_Tests/Pods-Franz_Tests.debug.xcconfig"; sourceTree = ""; }; + 667C2D4119F9F1D1918C7601 /* Pods_Franz_Tests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Franz_Tests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 6C3323F089BC766914CED93D /* Pods_Franz_Example.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Franz_Example.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 7225E292ED0C62BA09CEE0F4 /* Pods-Franz_Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Franz_Example.release.xcconfig"; path = "Pods/Target Support Files/Pods-Franz_Example/Pods-Franz_Example.release.xcconfig"; sourceTree = ""; }; + 9D745ED2B3FE3059BD423BDB /* Pods-Franz_Tests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Franz_Tests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Franz_Tests/Pods-Franz_Tests.debug.xcconfig"; sourceTree = ""; }; CDB7360B9EF0BBA225E2D7E1 /* Franz.podspec */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = Franz.podspec; path = ../Franz.podspec; sourceTree = ""; }; + D70445CB57D973F6D472057D /* Pods-Franz_Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Franz_Example.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Franz_Example/Pods-Franz_Example.debug.xcconfig"; sourceTree = ""; }; + E52EA036957BCF3C63540B62 /* Pods-Franz_Tests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Franz_Tests.release.xcconfig"; path = "Pods/Target Support Files/Pods-Franz_Tests/Pods-Franz_Tests.release.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -55,51 +39,21 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 76299E1A1C5587AF00C4EDE1 /* Pods_Franz_Example.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 607FACE21AFB9204008FA782 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 9EC11FF172797A9FE41EB395 /* Pods_Franz_Tests.framework in Frameworks */, + 3016416A56168C8638103E14 /* Pods_Franz_Example.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 058B4A17EBDC5043D0380DAB /* Pods */ = { - isa = PBXGroup; - children = ( - 4A93C2D572B5914E568B1536 /* Pods-Franz_Example.debug.xcconfig */, - BDE1F5B5CEEF3744FE48D035 /* Pods-Franz_Example.release.xcconfig */, - C3F5A52FA1C23AA2BC138DBD /* Pods-Franz_Tests.debug.xcconfig */, - 5AEC21E89DB8D5C0680DC1CB /* Pods-Franz_Tests.release.xcconfig */, - ); - name = Pods; - sourceTree = ""; - }; - 58673934660D036494032214 /* Frameworks */ = { - isa = PBXGroup; - children = ( - 76299E191C5587AF00C4EDE1 /* Pods_Franz_Example.framework */, - 75D0B0B27AA0D734FC02B7E0 /* Pods_Franz_Example.framework */, - 7E5F9C962BD5A14B1C4CA01D /* Pods_Franz_Tests.framework */, - ); - name = Frameworks; - sourceTree = ""; - }; 607FACC71AFB9204008FA782 = { isa = PBXGroup; children = ( 607FACF51AFB993E008FA782 /* Podspec Metadata */, 607FACD21AFB9204008FA782 /* Example for Franz */, - 607FACE81AFB9204008FA782 /* Tests */, 607FACD11AFB9204008FA782 /* Products */, - 058B4A17EBDC5043D0380DAB /* Pods */, - 58673934660D036494032214 /* Frameworks */, + 6709876332EE579265DA51D0 /* Pods */, + A6D111368CD085E7A557B83E /* Frameworks */, ); sourceTree = ""; }; @@ -107,7 +61,6 @@ isa = PBXGroup; children = ( 607FACD01AFB9204008FA782 /* Franz_Example.app */, - 607FACE51AFB9204008FA782 /* Franz_Tests.xctest */, ); name = Products; sourceTree = ""; @@ -134,31 +87,34 @@ name = "Supporting Files"; sourceTree = ""; }; - 607FACE81AFB9204008FA782 /* Tests */ = { + 607FACF51AFB993E008FA782 /* Podspec Metadata */ = { isa = PBXGroup; children = ( - 607FACEB1AFB9204008FA782 /* Tests.swift */, - 607FACE91AFB9204008FA782 /* Supporting Files */, + CDB7360B9EF0BBA225E2D7E1 /* Franz.podspec */, + 3F1786530256FB4A58B4F4D8 /* README.md */, + 1FB3D3E1489F609397269983 /* LICENSE */, ); - path = Tests; + name = "Podspec Metadata"; sourceTree = ""; }; - 607FACE91AFB9204008FA782 /* Supporting Files */ = { + 6709876332EE579265DA51D0 /* Pods */ = { isa = PBXGroup; children = ( - 607FACEA1AFB9204008FA782 /* Info.plist */, + D70445CB57D973F6D472057D /* Pods-Franz_Example.debug.xcconfig */, + 7225E292ED0C62BA09CEE0F4 /* Pods-Franz_Example.release.xcconfig */, + 9D745ED2B3FE3059BD423BDB /* Pods-Franz_Tests.debug.xcconfig */, + E52EA036957BCF3C63540B62 /* Pods-Franz_Tests.release.xcconfig */, ); - name = "Supporting Files"; + name = Pods; sourceTree = ""; }; - 607FACF51AFB993E008FA782 /* Podspec Metadata */ = { + A6D111368CD085E7A557B83E /* Frameworks */ = { isa = PBXGroup; children = ( - CDB7360B9EF0BBA225E2D7E1 /* Franz.podspec */, - 3F1786530256FB4A58B4F4D8 /* README.md */, - 1FB3D3E1489F609397269983 /* LICENSE */, + 6C3323F089BC766914CED93D /* Pods_Franz_Example.framework */, + 667C2D4119F9F1D1918C7601 /* Pods_Franz_Tests.framework */, ); - name = "Podspec Metadata"; + name = Frameworks; sourceTree = ""; }; /* End PBXGroup section */ @@ -168,12 +124,12 @@ isa = PBXNativeTarget; buildConfigurationList = 607FACEF1AFB9204008FA782 /* Build configuration list for PBXNativeTarget "Franz_Example" */; buildPhases = ( - 61CBD28CCAEC55E8E97B6549 /* Check Pods Manifest.lock */, + 465DFDC220236332AB155987 /* [CP] Check Pods Manifest.lock */, 607FACCC1AFB9204008FA782 /* Sources */, 607FACCD1AFB9204008FA782 /* Frameworks */, 607FACCE1AFB9204008FA782 /* Resources */, - F59794DD67357AB734556881 /* Embed Pods Frameworks */, - FB2AB3ECCCD21A32FE417FAD /* Copy Pods Resources */, + D31EC681960A4584346B27AD /* [CP] Embed Pods Frameworks */, + 23B6171CFB517C8D56D18C0E /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -184,27 +140,6 @@ productReference = 607FACD01AFB9204008FA782 /* Franz_Example.app */; productType = "com.apple.product-type.application"; }; - 607FACE41AFB9204008FA782 /* Franz_Tests */ = { - isa = PBXNativeTarget; - buildConfigurationList = 607FACF21AFB9204008FA782 /* Build configuration list for PBXNativeTarget "Franz_Tests" */; - buildPhases = ( - 19F8586D902DC486E019DAFB /* Check Pods Manifest.lock */, - 607FACE11AFB9204008FA782 /* Sources */, - 607FACE21AFB9204008FA782 /* Frameworks */, - 607FACE31AFB9204008FA782 /* Resources */, - A439EB34726DA678DEEC0A76 /* Embed Pods Frameworks */, - A162FD87A7D41BDE5F7258E6 /* Copy Pods Resources */, - ); - buildRules = ( - ); - dependencies = ( - 607FACE71AFB9204008FA782 /* PBXTargetDependency */, - ); - name = Franz_Tests; - productName = Tests; - productReference = 607FACE51AFB9204008FA782 /* Franz_Tests.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ @@ -212,15 +147,12 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0720; - LastUpgradeCheck = 0720; + LastUpgradeCheck = 0900; ORGANIZATIONNAME = CocoaPods; TargetAttributes = { 607FACCF1AFB9204008FA782 = { CreatedOnToolsVersion = 6.3.1; - }; - 607FACE41AFB9204008FA782 = { - CreatedOnToolsVersion = 6.3.1; - TestTargetID = 607FACCF1AFB9204008FA782; + LastSwiftMigration = 0830; }; }; }; @@ -238,7 +170,6 @@ projectRoot = ""; targets = ( 607FACCF1AFB9204008FA782 /* Franz_Example */, - 607FACE41AFB9204008FA782 /* Franz_Tests */, ); }; /* End PBXProject section */ @@ -254,84 +185,47 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - 607FACE31AFB9204008FA782 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 19F8586D902DC486E019DAFB /* Check Pods Manifest.lock */ = { + 23B6171CFB517C8D56D18C0E /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); - name = "Check Pods Manifest.lock"; + name = "[CP] Copy Pods Resources"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; - showEnvVarsInLog = 0; - }; - 61CBD28CCAEC55E8E97B6549 /* Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "Check Pods Manifest.lock"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; - showEnvVarsInLog = 0; - }; - A162FD87A7D41BDE5F7258E6 /* Copy Pods Resources */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "Copy Pods Resources"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Franz_Tests/Pods-Franz_Tests-resources.sh\"\n"; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Franz_Example/Pods-Franz_Example-resources.sh\"\n"; showEnvVarsInLog = 0; }; - A439EB34726DA678DEEC0A76 /* Embed Pods Frameworks */ = { + 465DFDC220236332AB155987 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); - name = "Embed Pods Frameworks"; + name = "[CP] Check Pods Manifest.lock"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Franz_Tests/Pods-Franz_Tests-frameworks.sh\"\n"; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n"; showEnvVarsInLog = 0; }; - F59794DD67357AB734556881 /* Embed Pods Frameworks */ = { + D31EC681960A4584346B27AD /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); - name = "Embed Pods Frameworks"; + name = "[CP] Embed Pods Frameworks"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; @@ -339,21 +233,6 @@ shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Franz_Example/Pods-Franz_Example-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; - FB2AB3ECCCD21A32FE417FAD /* Copy Pods Resources */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "Copy Pods Resources"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Franz_Example/Pods-Franz_Example-resources.sh\"\n"; - showEnvVarsInLog = 0; - }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -366,24 +245,8 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - 607FACE11AFB9204008FA782 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 607FACEC1AFB9204008FA782 /* Tests.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; /* End PBXSourcesBuildPhase section */ -/* Begin PBXTargetDependency section */ - 607FACE71AFB9204008FA782 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 607FACCF1AFB9204008FA782 /* Franz_Example */; - targetProxy = 607FACE61AFB9204008FA782 /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - /* Begin PBXVariantGroup section */ 607FACD91AFB9204008FA782 /* Main.storyboard */ = { isa = PBXVariantGroup; @@ -412,13 +275,21 @@ CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; @@ -457,13 +328,21 @@ CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; @@ -482,86 +361,53 @@ IPHONEOS_DEPLOYMENT_TARGET = 9.2; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; VALIDATE_PRODUCT = YES; }; name = Release; }; 607FACF01AFB9204008FA782 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 4A93C2D572B5914E568B1536 /* Pods-Franz_Example.debug.xcconfig */; + baseConfigurationReference = D70445CB57D973F6D472057D /* Pods-Franz_Example.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + DEVELOPMENT_TEAM = ""; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/build/Debug-iphoneos", ); INFOPLIST_FILE = Franz/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 9.2; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; MODULE_NAME = ExampleApp; PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; }; 607FACF11AFB9204008FA782 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = BDE1F5B5CEEF3744FE48D035 /* Pods-Franz_Example.release.xcconfig */; + baseConfigurationReference = 7225E292ED0C62BA09CEE0F4 /* Pods-Franz_Example.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + DEVELOPMENT_TEAM = ""; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/build/Debug-iphoneos", ); INFOPLIST_FILE = Franz/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 9.2; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; MODULE_NAME = ExampleApp; PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Release; }; - 607FACF31AFB9204008FA782 /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = C3F5A52FA1C23AA2BC138DBD /* Pods-Franz_Tests.debug.xcconfig */; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - FRAMEWORK_SEARCH_PATHS = ( - "$(SDKROOT)/Developer/Library/Frameworks", - "$(inherited)", - ); - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - INFOPLIST_FILE = Tests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.$(PRODUCT_NAME:rfc1034identifier)"; - PRODUCT_NAME = "$(TARGET_NAME)"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Franz_Example.app/Franz_Example"; - }; - name = Debug; - }; - 607FACF41AFB9204008FA782 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 5AEC21E89DB8D5C0680DC1CB /* Pods-Franz_Tests.release.xcconfig */; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - FRAMEWORK_SEARCH_PATHS = ( - "$(SDKROOT)/Developer/Library/Frameworks", - "$(inherited)", - ); - INFOPLIST_FILE = Tests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.$(PRODUCT_NAME:rfc1034identifier)"; - PRODUCT_NAME = "$(TARGET_NAME)"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Franz_Example.app/Franz_Example"; - }; - name = Release; - }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -583,15 +429,6 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 607FACF21AFB9204008FA782 /* Build configuration list for PBXNativeTarget "Franz_Tests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 607FACF31AFB9204008FA782 /* Debug */, - 607FACF41AFB9204008FA782 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; /* End XCConfigurationList section */ }; rootObject = 607FACC81AFB9204008FA782 /* Project object */; diff --git a/Example/Franz.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Cocoapods Example/Franz.xcodeproj/project.xcworkspace/contents.xcworkspacedata similarity index 100% rename from Example/Franz.xcodeproj/project.xcworkspace/contents.xcworkspacedata rename to Cocoapods Example/Franz.xcodeproj/project.xcworkspace/contents.xcworkspacedata diff --git a/Example/Franz.xcodeproj/xcshareddata/xcschemes/Franz-Example.xcscheme b/Cocoapods Example/Franz.xcodeproj/xcshareddata/xcschemes/Franz-Example.xcscheme similarity index 98% rename from Example/Franz.xcodeproj/xcshareddata/xcschemes/Franz-Example.xcscheme rename to Cocoapods Example/Franz.xcodeproj/xcshareddata/xcschemes/Franz-Example.xcscheme index ee77964..06f03bb 100644 --- a/Example/Franz.xcodeproj/xcshareddata/xcschemes/Franz-Example.xcscheme +++ b/Cocoapods Example/Franz.xcodeproj/xcshareddata/xcschemes/Franz-Example.xcscheme @@ -1,6 +1,6 @@ "../" +end + diff --git a/Cocoapods Example/Podfile.lock b/Cocoapods Example/Podfile.lock new file mode 100644 index 0000000..61b5f54 --- /dev/null +++ b/Cocoapods Example/Podfile.lock @@ -0,0 +1,16 @@ +PODS: + - Franz (0.1.0) + +DEPENDENCIES: + - Franz (from `../`) + +EXTERNAL SOURCES: + Franz: + :path: "../" + +SPEC CHECKSUMS: + Franz: '099af2ab9705201bc585ed3e4de8a2b703059dac' + +PODFILE CHECKSUM: 2cde30658617a6dbe8248094a35efba117c6dac5 + +COCOAPODS: 1.2.1 diff --git a/DockerTests/ConsumerTests.swift b/DockerTests/ConsumerTests.swift new file mode 100644 index 0000000..ed52e72 --- /dev/null +++ b/DockerTests/ConsumerTests.swift @@ -0,0 +1,138 @@ +// +// ConsumerTests.swift +// FranzTests +// +// Created by Luke Lau on 01/08/2017. +// + +import XCTest +import Franz + +class ConsumerTests: DockerTestBase { + + var cluster: Cluster! + + override func setUp() { + cluster = Cluster(brokers: [("localhost", 9092)], clientId: "testClient") + } + + func testReceive() { + let helloExpectation = expectation(description: "Receives 'Hello' message"), + worldExpectation = expectation(description: "Receives 'World' message"), + 💯Expectation = expectation(description: "Receives '💯' message") + + let consumer = cluster.getConsumer(topics: ["test"], groupId: "testGroup") + consumer.listen { message in + let string = String(data: message.value, encoding: .utf8) + if string == "Hello" { + helloExpectation.fulfill() + } + if string == "World" { + worldExpectation.fulfill() + } + if string == "💯" { + 💯Expectation.fulfill() + } + } + + cluster.sendMessage("test", message: "Hello") + + wait(for: [helloExpectation], timeout: 10) + + cluster.sendMessage("test", message: "World") + cluster.sendMessage("test", message: "💯") + + wait(for: [worldExpectation, 💯Expectation], timeout: 10) + } + + func testCount() { + let expectations = (0..<64).map { expectation(description: "Receives '\($0)'") } + let consumer = cluster.getConsumer(topics: ["test"], groupId: "testGroup") + consumer.listen { message in + let i = Int(String(data: message.value, encoding: .utf8)!)! + print("Consumed \(i)") + expectations[i].fulfill() + } + + for i in 0..<64 { + Timer.scheduledTimer(withTimeInterval: TimeInterval(i) / 2, repeats: false) { _ in + self.cluster.sendMessage("test", message: "\(i)") + print("Produced \(i)") + } + } + + waitForExpectations(timeout: 60) + } + + func testConsumerStops() { + let consumer = cluster.getConsumer(topics: ["stop"], groupId: "newgroup") + + let e = expectation(description: "Receives message from start") + + consumer.listen { message in + if String(data: message.value, encoding: .utf8)! == "test" { + e.fulfill() + } else { + XCTFail("Shouldn't have recevied message") + } + } + + cluster.sendMessage("stop", message: "test") + + waitForExpectations(timeout: 10) + + consumer.stop() + + DispatchQueue.global().asyncAfter(deadline: .now() + 3) { + self.cluster.sendMessage("stop", message: "stop") + } + + Thread.sleep(forTimeInterval: 10) + } + + func testFromStart() { + cluster.sendMessage("fromStart", message: "test") + + let consumer1 = cluster.getConsumer(topics: ["fromStart"], groupId: "newgroup") + + let first = expectation(description: "Receives message from start") + let second = expectation(description: "Receives second message") + + consumer1.listen(fromStart: true) { message in + if String(data: message.value, encoding: .utf8)! == "test" { + first.fulfill() + } + } + + let consumer2 = cluster.getConsumer(topics: ["fromStart"], groupId: "newGroup") + + consumer2.listen(fromStart: false) { message in + if String(data: message.value, encoding: .utf8)! == "second" { + second.fulfill() + } else { + XCTFail("Shouldn't have received any messages from earlier") + } + } + + wait(for: [first], timeout: 10) + + cluster.sendMessage("fromStart", message: "second") + + wait(for: [second], timeout: 10) + } + + func testDoesntReceiveUnsubscribedTopics() { + + let consumer = cluster.getConsumer(topics: ["foo"], groupId: "newgroup") + + consumer.listen { _ in + XCTFail("Shouldn't have received a message") + } + + cluster.sendMessage("test", message: "Foo") + + Thread.sleep(forTimeInterval: 30) + } + +} + diff --git a/DockerTests/DockerTestBase.swift b/DockerTests/DockerTestBase.swift new file mode 100644 index 0000000..6f11ec6 --- /dev/null +++ b/DockerTests/DockerTestBase.swift @@ -0,0 +1,145 @@ +// +// DockerTestBase.swift +// FranzTests +// +// Created by Luke Lau on 21/08/2017. +// + +import XCTest +@testable import Franz + +class DockerTestBase: XCTestCase { + + static var docker: Process! + static let startedSemaphore = DispatchSemaphore(value: 0) + + static let compose = URL(fileURLWithPath: "/usr/local/bin/docker-compose") + static let yml = Bundle(for: DockerTestBase.self).url(forResource: "docker-compose", withExtension: "yml")! + + override class func setUp() { + do { + + if #available(OSX 10.13, *) { + try Process.run(compose, arguments: ["-f", yml.path, "stop"]).waitUntilExit() + try Process.run(compose, arguments: ["-f", yml.path, "rm", "-f"]).waitUntilExit() + try docker = Process.run(compose, arguments: ["-f", yml.path, "up", "-d"]) + } else { + Process.launchedProcess(launchPath: compose.path, arguments: ["-f", yml.path, "stop"]).waitUntilExit() + Process.launchedProcess(launchPath: compose.path, arguments: ["-f", yml.path, "rm", "-f"]).waitUntilExit() + docker = Process.launchedProcess(launchPath: compose.path, arguments: ["-f", yml.path, "up", "-d"]) + } + + waitForKafka() + + } catch { + print("Docker must be installed") + } + } + + class func waitForKafka() { + let delegate = DockerStreamDelegate() + var inputStream: InputStream?, outputStream: OutputStream? + var timer: Timer? + + var runLoopToStop: CFRunLoop! + + DispatchQueue(label: "dockerStreamPoll").async { + if #available(OSX 10.12, *) { + timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { _ in + + if !delegate.retry { + return + } + + delegate.written = false + delegate.retry = false + + inputStream?.close() + outputStream?.close() + + Stream.getStreamsToHost(withName: "localhost", port: 9092, inputStream: &inputStream, outputStream: &outputStream) + + inputStream!.delegate = delegate + outputStream!.delegate = delegate + + delegate.inputStream = inputStream + delegate.outputStream = outputStream + + inputStream!.schedule(in: .current, forMode: .commonModes) + outputStream!.schedule(in: .current, forMode: .commonModes) + + inputStream!.open() + outputStream!.open() + } + } else { + fatalError("Upgrade to macOS 10.12 to run Docker tests") + } + runLoopToStop = CFRunLoopGetCurrent() + CFRunLoopRun() + } + startedSemaphore.wait() + + CFRunLoopStop(runLoopToStop) + + print("Kafka server is ready") + + inputStream?.close() + outputStream?.close() + + timer?.invalidate() + } + + class DockerStreamDelegate: NSObject, StreamDelegate { + + var inputStream: InputStream!, outputStream: OutputStream! + var retry = true + var written = false + + func stream(_ aStream: Stream, handle eventCode: Stream.Event) { + if eventCode == .errorOccurred { + retry = true + } + if aStream == inputStream, eventCode == .hasBytesAvailable { + + //Attempt to read in the size of the list groups request + var data = Data(capacity: 4) + data.withUnsafeMutableBytes { (bytes: UnsafeMutablePointer) -> Void in + + inputStream.read(bytes, maxLength: 4) + var readData = Data(buffer: UnsafeBufferPointer(start: bytes, count: 4)) + let responseSize = UInt32(data: &readData) + + //If it's a zero-length response, retry + if responseSize > 0 { + startedSemaphore.signal() + } else { + retry = true + } + } + } + if aStream == outputStream, eventCode == .hasSpaceAvailable, !written { + print("Trying to contact Kafka server") + let req = ListGroupsRequest() + req.clientId = "test" + req.data.withUnsafeBytes { (bytes: UnsafePointer) -> Void in + outputStream.write(bytes, maxLength: req.data.count) + } + written = true + } + } + + } + + override class func tearDown() { + do { + if #available(OSX 10.13, *) { + try Process.run(compose, arguments: ["-f", yml.path, "stop"]).waitUntilExit() + } else { + Process.launchedProcess(launchPath: compose.path, arguments: ["-f", yml.path, "stop"]).waitUntilExit() + } + } catch { + print("Failed to stop containers") + } + } + +} diff --git a/Example/Tests/Info.plist b/DockerTests/Info.plist similarity index 89% rename from Example/Tests/Info.plist rename to DockerTests/Info.plist index ba72822..6c40a6c 100644 --- a/Example/Tests/Info.plist +++ b/DockerTests/Info.plist @@ -3,7 +3,7 @@ CFBundleDevelopmentRegion - en + $(DEVELOPMENT_LANGUAGE) CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier @@ -16,8 +16,6 @@ BNDL CFBundleShortVersionString 1.0 - CFBundleSignature - ???? CFBundleVersion 1 diff --git a/DockerTests/docker-compose.yml b/DockerTests/docker-compose.yml new file mode 100644 index 0000000..b7e55b1 --- /dev/null +++ b/DockerTests/docker-compose.yml @@ -0,0 +1,15 @@ +version: '3' +services: + zookeeper: + image: zookeeper + ports: + - "2181:2181" + kafka: + image: wurstmeister/kafka + ports: + - "9092:9092" + environment: + - KAFKA_ADVERTISED_HOST_NAME=localhost + - KAFKA_ADVERTISED_PORT=9092 + - KAFKA_ZOOKEEPER_CONNECT=zookeeper:2181 + - KAFKA_CREATE_TOPICS=test:1:1;foo:1:1;bar:1:1;fromStart:1:1;stop:1:1; diff --git a/Example/Franz/AppDelegate.swift b/Example/Franz/AppDelegate.swift deleted file mode 100644 index 66b8b0d..0000000 --- a/Example/Franz/AppDelegate.swift +++ /dev/null @@ -1,46 +0,0 @@ -// -// AppDelegate.swift -// Franz -// -// Created by kellanburket on 01/24/2016. -// Copyright (c) 2016 kellanburket. All rights reserved. -// - -import UIKit - -@UIApplicationMain -class AppDelegate: UIResponder, UIApplicationDelegate { - - var window: UIWindow? - - - func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { - // Override point for customization after application launch. - return true - } - - func applicationWillResignActive(application: UIApplication) { - // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. - // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. - } - - func applicationDidEnterBackground(application: UIApplication) { - // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. - // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. - } - - func applicationWillEnterForeground(application: UIApplication) { - // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. - } - - func applicationDidBecomeActive(application: UIApplication) { - // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. - } - - func applicationWillTerminate(application: UIApplication) { - // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. - } - - -} - diff --git a/Example/Franz/ViewController.swift b/Example/Franz/ViewController.swift deleted file mode 100644 index 3de6e2f..0000000 --- a/Example/Franz/ViewController.swift +++ /dev/null @@ -1,118 +0,0 @@ -// -// ViewController.swift -// Franz -// -// Created by kellanburket on 01/24/2016. -// Copyright (c) 2016 kellanburket. All rights reserved. -// - -import UIKit -import Franz - -class ViewController: UIViewController, HighLevelConsumerDelegate { - - var cluster: Cluster! - - override func viewDidLoad() { - super.viewDidLoad() - - cluster = Cluster( - brokers: [ - ("127.0.0.1", 9092) - ], - clientId: "replica-test" - ) - - cluster.getHighLevelConsumer( - "replica", - partition: 0, - groupId: "replica-group", - delegate: self - ) - - dispatch_async(dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0)) { - while true { - self.cluster.batchMessage("replica", partition: 0, message: "1") - self.cluster.batchMessage("replica", partition: 0, message: "2") - self.cluster.batchMessage("replica", partition: 0, message: "3") - - do { - try self.cluster.sendBatch("replica", partition: 0) - } catch ClusterError.NoBatchForTopicPartition(let topic, let partition) { - print("Error: No Batch for Topic (\(topic)), (\(partition))") - break - } catch { - print("Error.") - break - } - sleep(1) - } - } - } - - func consumerDidReturnMessage(message: Message, offset: Int64) { - print(NSString(data: message.value, encoding: NSUTF8StringEncoding)) - } - - func shouldRetryFailedFetch( - topic: String, - partition: Int32, - offset: Int64, - errorId: Int16, - errorDescription: String - ) -> Bool { - return true - } - - func consumerIsReady(consumer: Consumer) { - print("Consumer is Ready") - if let highLevelConsumer = consumer as? HighLevelConsumer { - highLevelConsumer.poll() - } - } - - func topicPartitionLeaderNotFound(topic: String, partition: Int32) { - print("Topic Partition Leader Not Found") - } - - func shouldCommitOffset(topic: String, partition: Int32, offset: Int64) -> Bool { - print("Should Commit Offset") - return true - } - - func shouldAttachOffsetMetadata(topic: String, partition: Int32, offset: Int64) -> String? { - print("Should Attach Offset Metadata") - return "mooser" - } - - func offsetDidCommit(topic: String, partition: Int32, offset: Int64) { - print("Offset Did Commit") - } - - func offsetCommitDidFail( - topic: String, - partition: Int32, - offset: Int64, - errorId: Int16, - errorDescription: String - ) { - print("Offset Commit Did Fail") - } - - func shouldRetryFailedOffsetCommit( - topic: String, - partition: Int32, - offset: Int64, - errorId: Int16, - errorDescription: String - ) -> Bool { - return false - } - - override func didReceiveMemoryWarning() { - super.didReceiveMemoryWarning() - // Dispose of any resources that can be recreated. - } - -} - diff --git a/Example/Podfile b/Example/Podfile deleted file mode 100644 index b39ca7c..0000000 --- a/Example/Podfile +++ /dev/null @@ -1,12 +0,0 @@ -source 'https://github.com/CocoaPods/Specs.git' -use_frameworks! - -target 'Franz_Example', :exclusive => true do - pod "Franz", :path => "../" -end - -target 'Franz_Tests', :exclusive => true do - pod "Franz", :path => "../" - - pod 'FBSnapshotTestCase' -end diff --git a/Example/Podfile.lock b/Example/Podfile.lock deleted file mode 100644 index 3236575..0000000 --- a/Example/Podfile.lock +++ /dev/null @@ -1,21 +0,0 @@ -PODS: - - FBSnapshotTestCase (2.0.7): - - FBSnapshotTestCase/SwiftSupport (= 2.0.7) - - FBSnapshotTestCase/Core (2.0.7) - - FBSnapshotTestCase/SwiftSupport (2.0.7): - - FBSnapshotTestCase/Core - - Franz (0.1.0) - -DEPENDENCIES: - - FBSnapshotTestCase - - Franz (from `../`) - -EXTERNAL SOURCES: - Franz: - :path: "../" - -SPEC CHECKSUMS: - FBSnapshotTestCase: 7e85180d0d141a0cf472352edda7e80d7eaeb547 - Franz: 1cb521121a59a93d69f1c3466f0ac6b921f31f8b - -COCOAPODS: 0.39.0 diff --git a/Example/Pods/FBSnapshotTestCase/FBSnapshotTestCase.modulemap b/Example/Pods/FBSnapshotTestCase/FBSnapshotTestCase.modulemap deleted file mode 100644 index 733e78b..0000000 --- a/Example/Pods/FBSnapshotTestCase/FBSnapshotTestCase.modulemap +++ /dev/null @@ -1,15 +0,0 @@ -framework module FBSnapshotTestCase { - umbrella header "FBSnapshotTestCase.h" - - export * - module * { export * } - - header "FBSnapshotTestCase.h" - header "FBSnapshotTestCasePlatform.h" - header "FBSnapshotTestController.h" - - private header "UIImage+Compare.h" - private header "UIImage+Diff.h" - private header "UIImage+Snapshot.h" -} - diff --git a/Example/Pods/FBSnapshotTestCase/FBSnapshotTestCase/Categories/UIImage+Compare.h b/Example/Pods/FBSnapshotTestCase/FBSnapshotTestCase/Categories/UIImage+Compare.h deleted file mode 100644 index 9091d62..0000000 --- a/Example/Pods/FBSnapshotTestCase/FBSnapshotTestCase/Categories/UIImage+Compare.h +++ /dev/null @@ -1,37 +0,0 @@ -// -// Created by Gabriel Handford on 3/1/09. -// Copyright 2009-2013. All rights reserved. -// Created by John Boiles on 10/20/11. -// Copyright (c) 2011. All rights reserved -// Modified by Felix Schulze on 2/11/13. -// Copyright 2013. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the "Software"), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -#import - -@interface UIImage (Compare) - -- (BOOL)fb_compareWithImage:(UIImage *)image tolerance:(CGFloat)tolerance; - -@end diff --git a/Example/Pods/FBSnapshotTestCase/FBSnapshotTestCase/Categories/UIImage+Compare.m b/Example/Pods/FBSnapshotTestCase/FBSnapshotTestCase/Categories/UIImage+Compare.m deleted file mode 100644 index c997f57..0000000 --- a/Example/Pods/FBSnapshotTestCase/FBSnapshotTestCase/Categories/UIImage+Compare.m +++ /dev/null @@ -1,134 +0,0 @@ -// -// Created by Gabriel Handford on 3/1/09. -// Copyright 2009-2013. All rights reserved. -// Created by John Boiles on 10/20/11. -// Copyright (c) 2011. All rights reserved -// Modified by Felix Schulze on 2/11/13. -// Copyright 2013. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the "Software"), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -#import - -// This makes debugging much more fun -typedef union { - uint32_t raw; - unsigned char bytes[4]; - struct { - char red; - char green; - char blue; - char alpha; - } __attribute__ ((packed)) pixels; -} FBComparePixel; - -@implementation UIImage (Compare) - -- (BOOL)fb_compareWithImage:(UIImage *)image tolerance:(CGFloat)tolerance -{ - NSAssert(CGSizeEqualToSize(self.size, image.size), @"Images must be same size."); - - CGSize referenceImageSize = CGSizeMake(CGImageGetWidth(self.CGImage), CGImageGetHeight(self.CGImage)); - CGSize imageSize = CGSizeMake(CGImageGetWidth(image.CGImage), CGImageGetHeight(image.CGImage)); - - // The images have the equal size, so we could use the smallest amount of bytes because of byte padding - size_t minBytesPerRow = MIN(CGImageGetBytesPerRow(self.CGImage), CGImageGetBytesPerRow(image.CGImage)); - size_t referenceImageSizeBytes = referenceImageSize.height * minBytesPerRow; - void *referenceImagePixels = calloc(1, referenceImageSizeBytes); - void *imagePixels = calloc(1, referenceImageSizeBytes); - - if (!referenceImagePixels || !imagePixels) { - free(referenceImagePixels); - free(imagePixels); - return NO; - } - - CGContextRef referenceImageContext = CGBitmapContextCreate(referenceImagePixels, - referenceImageSize.width, - referenceImageSize.height, - CGImageGetBitsPerComponent(self.CGImage), - minBytesPerRow, - CGImageGetColorSpace(self.CGImage), - (CGBitmapInfo)kCGImageAlphaPremultipliedLast - ); - CGContextRef imageContext = CGBitmapContextCreate(imagePixels, - imageSize.width, - imageSize.height, - CGImageGetBitsPerComponent(image.CGImage), - minBytesPerRow, - CGImageGetColorSpace(image.CGImage), - (CGBitmapInfo)kCGImageAlphaPremultipliedLast - ); - - if (!referenceImageContext || !imageContext) { - CGContextRelease(referenceImageContext); - CGContextRelease(imageContext); - free(referenceImagePixels); - free(imagePixels); - return NO; - } - - CGContextDrawImage(referenceImageContext, CGRectMake(0, 0, referenceImageSize.width, referenceImageSize.height), self.CGImage); - CGContextDrawImage(imageContext, CGRectMake(0, 0, imageSize.width, imageSize.height), image.CGImage); - - CGContextRelease(referenceImageContext); - CGContextRelease(imageContext); - - BOOL imageEqual = YES; - - // Do a fast compare if we can - if (tolerance == 0) { - imageEqual = (memcmp(referenceImagePixels, imagePixels, referenceImageSizeBytes) == 0); - } else { - // Go through each pixel in turn and see if it is different - const NSInteger pixelCount = referenceImageSize.width * referenceImageSize.height; - - FBComparePixel *p1 = referenceImagePixels; - FBComparePixel *p2 = imagePixels; - - NSInteger numDiffPixels = 0; - for (int n = 0; n < pixelCount; ++n) { - // If this pixel is different, increment the pixel diff count and see - // if we have hit our limit. - if (p1->raw != p2->raw) { - numDiffPixels ++; - - CGFloat percent = (CGFloat)numDiffPixels / pixelCount; - if (percent > tolerance) { - imageEqual = NO; - break; - } - } - - p1++; - p2++; - } - } - - free(referenceImagePixels); - free(imagePixels); - - return imageEqual; -} - -@end diff --git a/Example/Pods/FBSnapshotTestCase/FBSnapshotTestCase/Categories/UIImage+Diff.h b/Example/Pods/FBSnapshotTestCase/FBSnapshotTestCase/Categories/UIImage+Diff.h deleted file mode 100644 index a0863f3..0000000 --- a/Example/Pods/FBSnapshotTestCase/FBSnapshotTestCase/Categories/UIImage+Diff.h +++ /dev/null @@ -1,37 +0,0 @@ -// -// Created by Gabriel Handford on 3/1/09. -// Copyright 2009-2013. All rights reserved. -// Created by John Boiles on 10/20/11. -// Copyright (c) 2011. All rights reserved -// Modified by Felix Schulze on 2/11/13. -// Copyright 2013. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the "Software"), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -#import - -@interface UIImage (Diff) - -- (UIImage *)fb_diffWithImage:(UIImage *)image; - -@end diff --git a/Example/Pods/FBSnapshotTestCase/FBSnapshotTestCase/Categories/UIImage+Diff.m b/Example/Pods/FBSnapshotTestCase/FBSnapshotTestCase/Categories/UIImage+Diff.m deleted file mode 100644 index ebb72fe..0000000 --- a/Example/Pods/FBSnapshotTestCase/FBSnapshotTestCase/Categories/UIImage+Diff.m +++ /dev/null @@ -1,56 +0,0 @@ -// -// Created by Gabriel Handford on 3/1/09. -// Copyright 2009-2013. All rights reserved. -// Created by John Boiles on 10/20/11. -// Copyright (c) 2011. All rights reserved -// Modified by Felix Schulze on 2/11/13. -// Copyright 2013. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the "Software"), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -#import - -@implementation UIImage (Diff) - -- (UIImage *)fb_diffWithImage:(UIImage *)image -{ - if (!image) { - return nil; - } - CGSize imageSize = CGSizeMake(MAX(self.size.width, image.size.width), MAX(self.size.height, image.size.height)); - UIGraphicsBeginImageContextWithOptions(imageSize, YES, 0); - CGContextRef context = UIGraphicsGetCurrentContext(); - [self drawInRect:CGRectMake(0, 0, self.size.width, self.size.height)]; - CGContextSetAlpha(context, 0.5); - CGContextBeginTransparencyLayer(context, NULL); - [image drawInRect:CGRectMake(0, 0, image.size.width, image.size.height)]; - CGContextSetBlendMode(context, kCGBlendModeDifference); - CGContextSetFillColorWithColor(context,[UIColor whiteColor].CGColor); - CGContextFillRect(context, CGRectMake(0, 0, self.size.width, self.size.height)); - CGContextEndTransparencyLayer(context); - UIImage *returnImage = UIGraphicsGetImageFromCurrentImageContext(); - UIGraphicsEndImageContext(); - return returnImage; -} - -@end diff --git a/Example/Pods/FBSnapshotTestCase/FBSnapshotTestCase/Categories/UIImage+Snapshot.h b/Example/Pods/FBSnapshotTestCase/FBSnapshotTestCase/Categories/UIImage+Snapshot.h deleted file mode 100644 index b0d5b26..0000000 --- a/Example/Pods/FBSnapshotTestCase/FBSnapshotTestCase/Categories/UIImage+Snapshot.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (c) 2015, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#import - -@interface UIImage (Snapshot) - -/// Uses renderInContext: to get a snapshot of the layer. -+ (UIImage *)fb_imageForLayer:(CALayer *)layer; - -/// Uses renderInContext: to get a snapshot of the view layer. -+ (UIImage *)fb_imageForViewLayer:(UIView *)view; - -/// Uses drawViewHierarchyInRect: to get a snapshot of the view and adds the view into a window if needed. -+ (UIImage *)fb_imageForView:(UIView *)view; - -@end diff --git a/Example/Pods/FBSnapshotTestCase/FBSnapshotTestCase/Categories/UIImage+Snapshot.m b/Example/Pods/FBSnapshotTestCase/FBSnapshotTestCase/Categories/UIImage+Snapshot.m deleted file mode 100644 index c792077..0000000 --- a/Example/Pods/FBSnapshotTestCase/FBSnapshotTestCase/Categories/UIImage+Snapshot.m +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2015, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#import - -@implementation UIImage (Snapshot) - -+ (UIImage *)fb_imageForLayer:(CALayer *)layer -{ - CGRect bounds = layer.bounds; - NSAssert1(CGRectGetWidth(bounds), @"Zero width for layer %@", layer); - NSAssert1(CGRectGetHeight(bounds), @"Zero height for layer %@", layer); - - UIGraphicsBeginImageContextWithOptions(bounds.size, NO, 0); - CGContextRef context = UIGraphicsGetCurrentContext(); - NSAssert1(context, @"Could not generate context for layer %@", layer); - CGContextSaveGState(context); - [layer layoutIfNeeded]; - [layer renderInContext:context]; - CGContextRestoreGState(context); - - UIImage *snapshot = UIGraphicsGetImageFromCurrentImageContext(); - UIGraphicsEndImageContext(); - return snapshot; -} - -+ (UIImage *)fb_imageForViewLayer:(UIView *)view -{ - [view layoutIfNeeded]; - return [self fb_imageForLayer:view.layer]; -} - -+ (UIImage *)fb_imageForView:(UIView *)view -{ - CGRect bounds = view.bounds; - NSAssert1(CGRectGetWidth(bounds), @"Zero width for view %@", view); - NSAssert1(CGRectGetHeight(bounds), @"Zero height for view %@", view); - - UIWindow *window = view.window; - if (window == nil) { - window = [[UIWindow alloc] initWithFrame:bounds]; - [window addSubview:view]; - [window makeKeyAndVisible]; - } - - UIGraphicsBeginImageContextWithOptions(bounds.size, NO, 0); - [view layoutIfNeeded]; - [view drawViewHierarchyInRect:view.bounds afterScreenUpdates:YES]; - - UIImage *snapshot = UIGraphicsGetImageFromCurrentImageContext(); - UIGraphicsEndImageContext(); - return snapshot; -} - -@end diff --git a/Example/Pods/FBSnapshotTestCase/FBSnapshotTestCase/FBSnapshotTestCase.h b/Example/Pods/FBSnapshotTestCase/FBSnapshotTestCase/FBSnapshotTestCase.h deleted file mode 100644 index 54e301e..0000000 --- a/Example/Pods/FBSnapshotTestCase/FBSnapshotTestCase/FBSnapshotTestCase.h +++ /dev/null @@ -1,200 +0,0 @@ -/* - * Copyright (c) 2015, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#import - -#import - -#import - -#import - -/* - There are three ways of setting reference image directories. - - 1. Set the preprocessor macro FB_REFERENCE_IMAGE_DIR to a double quoted - c-string with the path. - 2. Set an environment variable named FB_REFERENCE_IMAGE_DIR with the path. This - takes precedence over the preprocessor macro to allow for run-time override. - 3. Keep everything unset, which will cause the reference images to be looked up - inside the bundle holding the current test, in the - Resources/ReferenceImages_* directories. - */ -#ifndef FB_REFERENCE_IMAGE_DIR -#define FB_REFERENCE_IMAGE_DIR "" -#endif - -/** - Similar to our much-loved XCTAssert() macros. Use this to perform your test. No need to write an explanation, though. - @param view The view to snapshot - @param identifier An optional identifier, used if there are multiple snapshot tests in a given -test method. - @param suffixes An NSOrderedSet of strings for the different suffixes - @param tolerance The percentage of pixels that can differ and still count as an 'identical' view - */ -#define FBSnapshotVerifyViewWithOptions(view__, identifier__, suffixes__, tolerance__) \ - FBSnapshotVerifyViewOrLayerWithOptions(View, view__, identifier__, suffixes__, tolerance__) - -#define FBSnapshotVerifyView(view__, identifier__) \ - FBSnapshotVerifyViewWithOptions(view__, identifier__, FBSnapshotTestCaseDefaultSuffixes(), 0) - - -/** - Similar to our much-loved XCTAssert() macros. Use this to perform your test. No need to write an explanation, though. - @param layer The layer to snapshot - @param identifier An optional identifier, used is there are multiple snapshot tests in a given -test method. - @param suffixes An NSOrderedSet of strings for the different suffixes - @param tolerance The percentage of pixels that can differ and still count as an 'identical' layer - */ -#define FBSnapshotVerifyLayerWithOptions(layer__, identifier__, suffixes__, tolerance__) \ - FBSnapshotVerifyViewOrLayerWithOptions(Layer, layer__, identifier__, suffixes__, tolerance__) - -#define FBSnapshotVerifyLayer(layer__, identifier__) \ - FBSnapshotVerifyLayerWithOptions(layer__, identifier__, FBSnapshotTestCaseDefaultSuffixes(), 0) - - -#define FBSnapshotVerifyViewOrLayerWithOptions(what__, viewOrLayer__, identifier__, suffixes__, tolerance__) \ -{ \ - NSString *referenceImageDirectory = [self getReferenceImageDirectoryWithDefault:(@ FB_REFERENCE_IMAGE_DIR)]; \ - XCTAssertNotNil(referenceImageDirectory, @"Missing value for referenceImagesDirectory - Set FB_REFERENCE_IMAGE_DIR as Environment variable in your scheme.");\ - XCTAssertTrue((suffixes__.count > 0), @"Suffixes set cannot be empty %@", suffixes__); \ - \ - BOOL testSuccess__ = NO; \ - NSError *error__ = nil; \ - NSMutableArray *errors__ = [NSMutableArray array]; \ - \ - if (self.recordMode) { \ - \ - NSString *referenceImagesDirectory__ = [NSString stringWithFormat:@"%@%@", referenceImageDirectory, suffixes__.firstObject]; \ - BOOL referenceImageSaved__ = [self compareSnapshotOf ## what__ :(viewOrLayer__) referenceImagesDirectory:referenceImagesDirectory__ identifier:(identifier__) tolerance:(tolerance__) error:&error__]; \ - if (!referenceImageSaved__) { \ - [errors__ addObject:error__]; \ - } \ - } else { \ - \ - for (NSString *suffix__ in suffixes__) { \ - NSString *referenceImagesDirectory__ = [NSString stringWithFormat:@"%@%@", referenceImageDirectory, suffix__]; \ - BOOL referenceImageAvailable = [self referenceImageRecordedInDirectory:referenceImagesDirectory__ identifier:(identifier__) error:&error__]; \ - \ - if (referenceImageAvailable) { \ - BOOL comparisonSuccess__ = [self compareSnapshotOf ## what__ :(viewOrLayer__) referenceImagesDirectory:referenceImagesDirectory__ identifier:(identifier__) tolerance:(tolerance__) error:&error__]; \ - [errors__ removeAllObjects]; \ - if (comparisonSuccess__) { \ - testSuccess__ = YES; \ - break; \ - } else { \ - [errors__ addObject:error__]; \ - } \ - } else { \ - [errors__ addObject:error__]; \ - } \ - } \ - } \ - XCTAssertTrue(testSuccess__, @"Snapshot comparison failed: %@", errors__.firstObject); \ - XCTAssertFalse(self.recordMode, @"Test ran in record mode. Reference image is now saved. Disable record mode to perform an actual snapshot comparison!"); \ -} - - -/** - The base class of view snapshotting tests. If you have small UI component, it's often easier to configure it in a test - and compare an image of the view to a reference image that write lots of complex layout-code tests. - - In order to flip the tests in your subclass to record the reference images set @c recordMode to @c YES. - - @attention When recording, the reference image directory should be explicitly - set, otherwise the images may be written to somewhere inside the - simulator directory. - - For example: - @code - - (void)setUp - { - [super setUp]; - self.recordMode = YES; - } - @endcode - */ -@interface FBSnapshotTestCase : XCTestCase - -/** - When YES, the test macros will save reference images, rather than performing an actual test. - */ -@property (readwrite, nonatomic, assign) BOOL recordMode; - -/** - When @c YES appends the name of the device model and OS to the snapshot file name. - The default value is @c NO. - */ -@property (readwrite, nonatomic, assign, getter=isDeviceAgnostic) BOOL deviceAgnostic; - -/** - When YES, renders a snapshot of the complete view hierarchy as visible onscreen. - There are several things that do not work if renderInContext: is used. - - UIVisualEffect #70 - - UIAppearance #91 - - Size Classes #92 - - @attention If the view does't belong to a UIWindow, it will create one and add the view as a subview. - */ -@property (readwrite, nonatomic, assign) BOOL usesDrawViewHierarchyInRect; - -- (void)setUp NS_REQUIRES_SUPER; -- (void)tearDown NS_REQUIRES_SUPER; - -/** - Performs the comparison or records a snapshot of the layer if recordMode is YES. - @param layer The Layer to snapshot - @param referenceImagesDirectory The directory in which reference images are stored. - @param identifier An optional identifier, used if there are multiple snapshot tests in a given -test method. - @param tolerance The percentage difference to still count as identical - 0 mean pixel perfect, 1 means I don't care - @param errorPtr An error to log in an XCTAssert() macro if the method fails (missing reference image, images differ, etc). - @returns YES if the comparison (or saving of the reference image) succeeded. - */ -- (BOOL)compareSnapshotOfLayer:(CALayer *)layer - referenceImagesDirectory:(NSString *)referenceImagesDirectory - identifier:(NSString *)identifier - tolerance:(CGFloat)tolerance - error:(NSError **)errorPtr; - -/** - Performs the comparison or records a snapshot of the view if recordMode is YES. - @param view The view to snapshot - @param referenceImagesDirectory The directory in which reference images are stored. - @param identifier An optional identifier, used if there are multiple snapshot tests in a given -test method. - @param tolerance The percentage difference to still count as identical - 0 mean pixel perfect, 1 means I don't care - @param errorPtr An error to log in an XCTAssert() macro if the method fails (missing reference image, images differ, etc). - @returns YES if the comparison (or saving of the reference image) succeeded. - */ -- (BOOL)compareSnapshotOfView:(UIView *)view - referenceImagesDirectory:(NSString *)referenceImagesDirectory - identifier:(NSString *)identifier - tolerance:(CGFloat)tolerance - error:(NSError **)errorPtr; - -/** - Checks if reference image with identifier based name exists in the reference images directory. - @param referenceImagesDirectory The directory in which reference images are stored. - @param identifier An optional identifier, used if there are multiple snapshot tests in a given -test method. - @param errorPtr An error to log in an XCTAssert() macro if the method fails (missing reference image, images differ, etc). - @returns YES if reference image exists. - */ -- (BOOL)referenceImageRecordedInDirectory:(NSString *)referenceImagesDirectory - identifier:(NSString *)identifier - error:(NSError **)errorPtr; - -/** - Returns the reference image directory. - - Helper function used to implement the assert macros. - - @param dir directory to use if environment variable not specified. Ignored if null or empty. - */ -- (NSString *)getReferenceImageDirectoryWithDefault:(NSString *)dir; - -@end diff --git a/Example/Pods/FBSnapshotTestCase/FBSnapshotTestCase/FBSnapshotTestCase.m b/Example/Pods/FBSnapshotTestCase/FBSnapshotTestCase/FBSnapshotTestCase.m deleted file mode 100644 index 3ee351f..0000000 --- a/Example/Pods/FBSnapshotTestCase/FBSnapshotTestCase/FBSnapshotTestCase.m +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright (c) 2015, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#import -#import - -@implementation FBSnapshotTestCase -{ - FBSnapshotTestController *_snapshotController; -} - -#pragma mark - Overrides - -- (void)setUp -{ - [super setUp]; - _snapshotController = [[FBSnapshotTestController alloc] initWithTestName:NSStringFromClass([self class])]; -} - -- (void)tearDown -{ - _snapshotController = nil; - [super tearDown]; -} - -- (BOOL)recordMode -{ - return _snapshotController.recordMode; -} - -- (void)setRecordMode:(BOOL)recordMode -{ - NSAssert1(_snapshotController, @"%s cannot be called before [super setUp]", __FUNCTION__); - _snapshotController.recordMode = recordMode; -} - -- (BOOL)isDeviceAgnostic -{ - return _snapshotController.deviceAgnostic; -} - -- (void)setDeviceAgnostic:(BOOL)deviceAgnostic -{ - NSAssert1(_snapshotController, @"%s cannot be called before [super setUp]", __FUNCTION__); - _snapshotController.deviceAgnostic = deviceAgnostic; -} - -- (BOOL)usesDrawViewHierarchyInRect -{ - return _snapshotController.usesDrawViewHierarchyInRect; -} - -- (void)setUsesDrawViewHierarchyInRect:(BOOL)usesDrawViewHierarchyInRect -{ - NSAssert1(_snapshotController, @"%s cannot be called before [super setUp]", __FUNCTION__); - _snapshotController.usesDrawViewHierarchyInRect = usesDrawViewHierarchyInRect; -} - -#pragma mark - Public API - -- (BOOL)compareSnapshotOfLayer:(CALayer *)layer - referenceImagesDirectory:(NSString *)referenceImagesDirectory - identifier:(NSString *)identifier - tolerance:(CGFloat)tolerance - error:(NSError **)errorPtr -{ - return [self _compareSnapshotOfViewOrLayer:layer - referenceImagesDirectory:referenceImagesDirectory - identifier:identifier - tolerance:tolerance - error:errorPtr]; -} - -- (BOOL)compareSnapshotOfView:(UIView *)view - referenceImagesDirectory:(NSString *)referenceImagesDirectory - identifier:(NSString *)identifier - tolerance:(CGFloat)tolerance - error:(NSError **)errorPtr -{ - return [self _compareSnapshotOfViewOrLayer:view - referenceImagesDirectory:referenceImagesDirectory - identifier:identifier - tolerance:tolerance - error:errorPtr]; -} - -- (BOOL)referenceImageRecordedInDirectory:(NSString *)referenceImagesDirectory - identifier:(NSString *)identifier - error:(NSError **)errorPtr -{ - NSAssert1(_snapshotController, @"%s cannot be called before [super setUp]", __FUNCTION__); - _snapshotController.referenceImagesDirectory = referenceImagesDirectory; - UIImage *referenceImage = [_snapshotController referenceImageForSelector:self.invocation.selector - identifier:identifier - error:errorPtr]; - - return (referenceImage != nil); -} - -- (NSString *)getReferenceImageDirectoryWithDefault:(NSString *)dir -{ - NSString *envReferenceImageDirectory = [NSProcessInfo processInfo].environment[@"FB_REFERENCE_IMAGE_DIR"]; - if (envReferenceImageDirectory) { - return envReferenceImageDirectory; - } - if (dir && dir.length > 0) { - return dir; - } - return [[NSBundle bundleForClass:self.class].resourcePath stringByAppendingPathComponent:@"ReferenceImages"]; -} - - -#pragma mark - Private API - -- (BOOL)_compareSnapshotOfViewOrLayer:(id)viewOrLayer - referenceImagesDirectory:(NSString *)referenceImagesDirectory - identifier:(NSString *)identifier - tolerance:(CGFloat)tolerance - error:(NSError **)errorPtr -{ - _snapshotController.referenceImagesDirectory = referenceImagesDirectory; - return [_snapshotController compareSnapshotOfViewOrLayer:viewOrLayer - selector:self.invocation.selector - identifier:identifier - tolerance:tolerance - error:errorPtr]; -} - -@end diff --git a/Example/Pods/FBSnapshotTestCase/FBSnapshotTestCase/FBSnapshotTestCasePlatform.h b/Example/Pods/FBSnapshotTestCase/FBSnapshotTestCase/FBSnapshotTestCasePlatform.h deleted file mode 100644 index e04acf2..0000000 --- a/Example/Pods/FBSnapshotTestCase/FBSnapshotTestCase/FBSnapshotTestCasePlatform.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2015, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#import - -#ifdef __cplusplus -extern "C" { -#endif - -/** - Returns a Boolean value that indicates whether the snapshot test is running in 64Bit. - This method is a convenience for creating the suffixes set based on the architecture - that the test is running. - - @returns @c YES if the test is running in 64bit, otherwise @c NO. - */ -BOOL FBSnapshotTestCaseIs64Bit(void); - -/** - Returns a default set of strings that is used to append a suffix based on the architectures. - @warning Do not modify this function, you can create your own and use it with @c FBSnapshotVerifyViewWithOptions() - - @returns An @c NSOrderedSet object containing strings that are appended to the reference images directory. - */ -NSOrderedSet *FBSnapshotTestCaseDefaultSuffixes(void); - -/** - Returns a fully «normalized» file name. - Strips punctuation and spaces and replaces them with @c _. Also appends the device model, running OS and screen size to the file name. - - @returns An @c NSString object containing the passed @c fileName with the device model, OS and screen size appended at the end. - */ -NSString *FBDeviceAgnosticNormalizedFileName(NSString *fileName); - -#ifdef __cplusplus -} -#endif diff --git a/Example/Pods/FBSnapshotTestCase/FBSnapshotTestCase/FBSnapshotTestCasePlatform.m b/Example/Pods/FBSnapshotTestCase/FBSnapshotTestCase/FBSnapshotTestCasePlatform.m deleted file mode 100644 index 4f6fb01..0000000 --- a/Example/Pods/FBSnapshotTestCase/FBSnapshotTestCase/FBSnapshotTestCasePlatform.m +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2015, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#import -#import - -BOOL FBSnapshotTestCaseIs64Bit(void) -{ -#if __LP64__ - return YES; -#else - return NO; -#endif -} - -NSOrderedSet *FBSnapshotTestCaseDefaultSuffixes(void) -{ - NSMutableOrderedSet *suffixesSet = [[NSMutableOrderedSet alloc] init]; - [suffixesSet addObject:@"_32"]; - [suffixesSet addObject:@"_64"]; - if (FBSnapshotTestCaseIs64Bit()) { - return [suffixesSet reversedOrderedSet]; - } - return [suffixesSet copy]; -} - -NSString *FBDeviceAgnosticNormalizedFileName(NSString *fileName) -{ - UIDevice *device = [UIDevice currentDevice]; - CGSize screenSize = [[UIApplication sharedApplication] keyWindow].bounds.size; - NSString *os = device.systemVersion; - - fileName = [NSString stringWithFormat:@"%@_%@%@_%.0fx%.0f", fileName, device.model, os, screenSize.width, screenSize.height]; - - NSMutableCharacterSet *invalidCharacters = [NSMutableCharacterSet new]; - [invalidCharacters formUnionWithCharacterSet:[NSCharacterSet whitespaceCharacterSet]]; - [invalidCharacters formUnionWithCharacterSet:[NSCharacterSet punctuationCharacterSet]]; - NSArray *validComponents = [fileName componentsSeparatedByCharactersInSet:invalidCharacters]; - fileName = [validComponents componentsJoinedByString:@"_"]; - - return fileName; -} \ No newline at end of file diff --git a/Example/Pods/FBSnapshotTestCase/FBSnapshotTestCase/FBSnapshotTestController.h b/Example/Pods/FBSnapshotTestCase/FBSnapshotTestCase/FBSnapshotTestController.h deleted file mode 100644 index 5719aba..0000000 --- a/Example/Pods/FBSnapshotTestCase/FBSnapshotTestCase/FBSnapshotTestController.h +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Copyright (c) 2015, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#import -#import - -typedef NS_ENUM(NSInteger, FBSnapshotTestControllerErrorCode) { - FBSnapshotTestControllerErrorCodeUnknown, - FBSnapshotTestControllerErrorCodeNeedsRecord, - FBSnapshotTestControllerErrorCodePNGCreationFailed, - FBSnapshotTestControllerErrorCodeImagesDifferentSizes, - FBSnapshotTestControllerErrorCodeImagesDifferent, -}; -/** - Errors returned by the methods of FBSnapshotTestController use this domain. - */ -extern NSString *const FBSnapshotTestControllerErrorDomain; - -/** - Errors returned by the methods of FBSnapshotTestController sometimes contain this key in the `userInfo` dictionary. - */ -extern NSString *const FBReferenceImageFilePathKey; - -/** - Provides the heavy-lifting for FBSnapshotTestCase. It loads and saves images, along with performing the actual pixel- - by-pixel comparison of images. - Instances are initialized with the test class, and directories to read and write to. - */ -@interface FBSnapshotTestController : NSObject - -/** - Record snapshots. - */ -@property (readwrite, nonatomic, assign) BOOL recordMode; - -/** - When @c YES appends the name of the device model and OS to the snapshot file name. - The default value is @c NO. - */ -@property (readwrite, nonatomic, assign, getter=isDeviceAgnostic) BOOL deviceAgnostic; - -/** - Uses drawViewHierarchyInRect:afterScreenUpdates: to draw the image instead of renderInContext: - */ -@property (readwrite, nonatomic, assign) BOOL usesDrawViewHierarchyInRect; - -/** - The directory in which referfence images are stored. - */ -@property (readwrite, nonatomic, copy) NSString *referenceImagesDirectory; - -/** - @param testClass The subclass of FBSnapshotTestCase that is using this controller. - @returns An instance of FBSnapshotTestController. - */ -- (instancetype)initWithTestClass:(Class)testClass; - -/** - Designated initializer. - @param testName The name of the tests. - @returns An instance of FBSnapshotTestController. - */ -- (instancetype)initWithTestName:(NSString *)testName; - -/** - Performs the comparison of the layer. - @param layer The Layer to snapshot. - @param selector The test method being run. - @param identifier An optional identifier, used is there are muliptle snapshot tests in a given -test method. - @param error An error to log in an XCTAssert() macro if the method fails (missing reference image, images differ, etc). - @returns YES if the comparison (or saving of the reference image) succeeded. - */ -- (BOOL)compareSnapshotOfLayer:(CALayer *)layer - selector:(SEL)selector - identifier:(NSString *)identifier - error:(NSError **)errorPtr; - -/** - Performs the comparison of the view. - @param view The view to snapshot. - @param selector The test method being run. - @param identifier An optional identifier, used is there are muliptle snapshot tests in a given -test method. - @param error An error to log in an XCTAssert() macro if the method fails (missing reference image, images differ, etc). - @returns YES if the comparison (or saving of the reference image) succeeded. - */ -- (BOOL)compareSnapshotOfView:(UIView *)view - selector:(SEL)selector - identifier:(NSString *)identifier - error:(NSError **)errorPtr; - -/** - Performs the comparison of a view or layer. - @param view The view or layer to snapshot. - @param selector The test method being run. - @param identifier An optional identifier, used is there are muliptle snapshot tests in a given -test method. - @param tolerance The percentage of pixels that can differ and still be considered 'identical' - @param error An error to log in an XCTAssert() macro if the method fails (missing reference image, images differ, etc). - @returns YES if the comparison (or saving of the reference image) succeeded. - */ -- (BOOL)compareSnapshotOfViewOrLayer:(id)viewOrLayer - selector:(SEL)selector - identifier:(NSString *)identifier - tolerance:(CGFloat)tolerance - error:(NSError **)errorPtr; - -/** - Loads a reference image. - @param selector The test method being run. - @param identifier The optional identifier, used when multiple images are tested in a single -test method. - @param errorPtr An error, if this methods returns nil, the error will be something useful. - @returns An image. - */ -- (UIImage *)referenceImageForSelector:(SEL)selector - identifier:(NSString *)identifier - error:(NSError **)errorPtr; - -/** - Performs a pixel-by-pixel comparison of the two images with an allowable margin of error. - @param referenceImage The reference (correct) image. - @param image The image to test against the reference. - @param tolerance The percentage of pixels that can differ and still be considered 'identical' - @param errorPtr An error that indicates why the comparison failed if it does. - @returns YES if the comparison succeeded and the images are the same(ish). - */ -- (BOOL)compareReferenceImage:(UIImage *)referenceImage - toImage:(UIImage *)image - tolerance:(CGFloat)tolerance - error:(NSError **)errorPtr; - -/** - Saves the reference image and the test image to `failedOutputDirectory`. - @param referenceImage The reference (correct) image. - @param testImage The image to test against the reference. - @param selector The test method being run. - @param identifier The optional identifier, used when multiple images are tested in a single -test method. - @param errorPtr An error that indicates why the comparison failed if it does. - @returns YES if the save succeeded. - */ -- (BOOL)saveFailedReferenceImage:(UIImage *)referenceImage - testImage:(UIImage *)testImage - selector:(SEL)selector - identifier:(NSString *)identifier - error:(NSError **)errorPtr; -@end diff --git a/Example/Pods/FBSnapshotTestCase/FBSnapshotTestCase/FBSnapshotTestController.m b/Example/Pods/FBSnapshotTestCase/FBSnapshotTestCase/FBSnapshotTestController.m deleted file mode 100644 index 4cebe10..0000000 --- a/Example/Pods/FBSnapshotTestCase/FBSnapshotTestCase/FBSnapshotTestController.m +++ /dev/null @@ -1,356 +0,0 @@ -/* - * Copyright (c) 2015, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#import -#import -#import -#import -#import - -#import - -NSString *const FBSnapshotTestControllerErrorDomain = @"FBSnapshotTestControllerErrorDomain"; -NSString *const FBReferenceImageFilePathKey = @"FBReferenceImageFilePathKey"; - -typedef NS_ENUM(NSUInteger, FBTestSnapshotFileNameType) { - FBTestSnapshotFileNameTypeReference, - FBTestSnapshotFileNameTypeFailedReference, - FBTestSnapshotFileNameTypeFailedTest, - FBTestSnapshotFileNameTypeFailedTestDiff, -}; - -@implementation FBSnapshotTestController -{ - NSString *_testName; - NSFileManager *_fileManager; -} - -#pragma mark - Initializers - -- (instancetype)initWithTestClass:(Class)testClass; -{ - return [self initWithTestName:NSStringFromClass(testClass)]; -} - -- (instancetype)initWithTestName:(NSString *)testName -{ - if (self = [super init]) { - _testName = [testName copy]; - _deviceAgnostic = NO; - - _fileManager = [[NSFileManager alloc] init]; - } - return self; -} - -#pragma mark - Overrides - -- (NSString *)description -{ - return [NSString stringWithFormat:@"%@ %@", [super description], _referenceImagesDirectory]; -} - -#pragma mark - Public API - -- (BOOL)compareSnapshotOfLayer:(CALayer *)layer - selector:(SEL)selector - identifier:(NSString *)identifier - error:(NSError **)errorPtr -{ - return [self compareSnapshotOfViewOrLayer:layer - selector:selector - identifier:identifier - tolerance:0 - error:errorPtr]; -} - -- (BOOL)compareSnapshotOfView:(UIView *)view - selector:(SEL)selector - identifier:(NSString *)identifier - error:(NSError **)errorPtr -{ - return [self compareSnapshotOfViewOrLayer:view - selector:selector - identifier:identifier - tolerance:0 - error:errorPtr]; -} - -- (BOOL)compareSnapshotOfViewOrLayer:(id)viewOrLayer - selector:(SEL)selector - identifier:(NSString *)identifier - tolerance:(CGFloat)tolerance - error:(NSError **)errorPtr -{ - if (self.recordMode) { - return [self _recordSnapshotOfViewOrLayer:viewOrLayer selector:selector identifier:identifier error:errorPtr]; - } else { - return [self _performPixelComparisonWithViewOrLayer:viewOrLayer selector:selector identifier:identifier tolerance:tolerance error:errorPtr]; - } -} - -- (UIImage *)referenceImageForSelector:(SEL)selector - identifier:(NSString *)identifier - error:(NSError **)errorPtr -{ - NSString *filePath = [self _referenceFilePathForSelector:selector identifier:identifier]; - UIImage *image = [UIImage imageWithContentsOfFile:filePath]; - if (nil == image && NULL != errorPtr) { - BOOL exists = [_fileManager fileExistsAtPath:filePath]; - if (!exists) { - *errorPtr = [NSError errorWithDomain:FBSnapshotTestControllerErrorDomain - code:FBSnapshotTestControllerErrorCodeNeedsRecord - userInfo:@{ - FBReferenceImageFilePathKey: filePath, - NSLocalizedDescriptionKey: @"Unable to load reference image.", - NSLocalizedFailureReasonErrorKey: @"Reference image not found. You need to run the test in record mode", - }]; - } else { - *errorPtr = [NSError errorWithDomain:FBSnapshotTestControllerErrorDomain - code:FBSnapshotTestControllerErrorCodeUnknown - userInfo:nil]; - } - } - return image; -} - -- (BOOL)compareReferenceImage:(UIImage *)referenceImage - toImage:(UIImage *)image - tolerance:(CGFloat)tolerance - error:(NSError **)errorPtr -{ - if (CGSizeEqualToSize(referenceImage.size, image.size)) { - BOOL imagesEqual = [referenceImage fb_compareWithImage:image tolerance:tolerance]; - if (NULL != errorPtr) { - *errorPtr = [NSError errorWithDomain:FBSnapshotTestControllerErrorDomain - code:FBSnapshotTestControllerErrorCodeImagesDifferent - userInfo:@{ - NSLocalizedDescriptionKey: @"Images different", - }]; - } - return imagesEqual; - } - if (NULL != errorPtr) { - *errorPtr = [NSError errorWithDomain:FBSnapshotTestControllerErrorDomain - code:FBSnapshotTestControllerErrorCodeImagesDifferentSizes - userInfo:@{ - NSLocalizedDescriptionKey: @"Images different sizes", - NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:@"referenceImage:%@, image:%@", - NSStringFromCGSize(referenceImage.size), - NSStringFromCGSize(image.size)], - }]; - } - return NO; -} - -- (BOOL)saveFailedReferenceImage:(UIImage *)referenceImage - testImage:(UIImage *)testImage - selector:(SEL)selector - identifier:(NSString *)identifier - error:(NSError **)errorPtr -{ - NSData *referencePNGData = UIImagePNGRepresentation(referenceImage); - NSData *testPNGData = UIImagePNGRepresentation(testImage); - - NSString *referencePath = [self _failedFilePathForSelector:selector - identifier:identifier - fileNameType:FBTestSnapshotFileNameTypeFailedReference]; - - NSError *creationError = nil; - BOOL didCreateDir = [_fileManager createDirectoryAtPath:[referencePath stringByDeletingLastPathComponent] - withIntermediateDirectories:YES - attributes:nil - error:&creationError]; - if (!didCreateDir) { - if (NULL != errorPtr) { - *errorPtr = creationError; - } - return NO; - } - - if (![referencePNGData writeToFile:referencePath options:NSDataWritingAtomic error:errorPtr]) { - return NO; - } - - NSString *testPath = [self _failedFilePathForSelector:selector - identifier:identifier - fileNameType:FBTestSnapshotFileNameTypeFailedTest]; - - if (![testPNGData writeToFile:testPath options:NSDataWritingAtomic error:errorPtr]) { - return NO; - } - - NSString *diffPath = [self _failedFilePathForSelector:selector - identifier:identifier - fileNameType:FBTestSnapshotFileNameTypeFailedTestDiff]; - - UIImage *diffImage = [referenceImage fb_diffWithImage:testImage]; - NSData *diffImageData = UIImagePNGRepresentation(diffImage); - - if (![diffImageData writeToFile:diffPath options:NSDataWritingAtomic error:errorPtr]) { - return NO; - } - - NSLog(@"If you have Kaleidoscope installed you can run this command to see an image diff:\n" - @"ksdiff \"%@\" \"%@\"", referencePath, testPath); - - return YES; -} - -#pragma mark - Private API - -- (NSString *)_fileNameForSelector:(SEL)selector - identifier:(NSString *)identifier - fileNameType:(FBTestSnapshotFileNameType)fileNameType -{ - NSString *fileName = nil; - switch (fileNameType) { - case FBTestSnapshotFileNameTypeFailedReference: - fileName = @"reference_"; - break; - case FBTestSnapshotFileNameTypeFailedTest: - fileName = @"failed_"; - break; - case FBTestSnapshotFileNameTypeFailedTestDiff: - fileName = @"diff_"; - break; - default: - fileName = @""; - break; - } - fileName = [fileName stringByAppendingString:NSStringFromSelector(selector)]; - if (0 < identifier.length) { - fileName = [fileName stringByAppendingFormat:@"_%@", identifier]; - } - - if (self.isDeviceAgnostic) { - fileName = FBDeviceAgnosticNormalizedFileName(fileName); - } - - if ([[UIScreen mainScreen] scale] > 1) { - fileName = [fileName stringByAppendingFormat:@"@%.fx", [[UIScreen mainScreen] scale]]; - } - fileName = [fileName stringByAppendingPathExtension:@"png"]; - return fileName; -} - -- (NSString *)_referenceFilePathForSelector:(SEL)selector - identifier:(NSString *)identifier -{ - NSString *fileName = [self _fileNameForSelector:selector - identifier:identifier - fileNameType:FBTestSnapshotFileNameTypeReference]; - NSString *filePath = [_referenceImagesDirectory stringByAppendingPathComponent:_testName]; - filePath = [filePath stringByAppendingPathComponent:fileName]; - return filePath; -} - -- (NSString *)_failedFilePathForSelector:(SEL)selector - identifier:(NSString *)identifier - fileNameType:(FBTestSnapshotFileNameType)fileNameType -{ - NSString *fileName = [self _fileNameForSelector:selector - identifier:identifier - fileNameType:fileNameType]; - NSString *folderPath = NSTemporaryDirectory(); - if (getenv("IMAGE_DIFF_DIR")) { - folderPath = @(getenv("IMAGE_DIFF_DIR")); - } - NSString *filePath = [folderPath stringByAppendingPathComponent:_testName]; - filePath = [filePath stringByAppendingPathComponent:fileName]; - return filePath; -} - -- (BOOL)_performPixelComparisonWithViewOrLayer:(id)viewOrLayer - selector:(SEL)selector - identifier:(NSString *)identifier - tolerance:(CGFloat)tolerance - error:(NSError **)errorPtr -{ - UIImage *referenceImage = [self referenceImageForSelector:selector identifier:identifier error:errorPtr]; - if (nil != referenceImage) { - UIImage *snapshot = [self _imageForViewOrLayer:viewOrLayer]; - BOOL imagesSame = [self compareReferenceImage:referenceImage toImage:snapshot tolerance:tolerance error:errorPtr]; - if (!imagesSame) { - [self saveFailedReferenceImage:referenceImage - testImage:snapshot - selector:selector - identifier:identifier - error:errorPtr]; - } - return imagesSame; - } - return NO; -} - -- (BOOL)_recordSnapshotOfViewOrLayer:(id)viewOrLayer - selector:(SEL)selector - identifier:(NSString *)identifier - error:(NSError **)errorPtr -{ - UIImage *snapshot = [self _imageForViewOrLayer:viewOrLayer]; - return [self _saveReferenceImage:snapshot selector:selector identifier:identifier error:errorPtr]; -} - -- (BOOL)_saveReferenceImage:(UIImage *)image - selector:(SEL)selector - identifier:(NSString *)identifier - error:(NSError **)errorPtr -{ - BOOL didWrite = NO; - if (nil != image) { - NSString *filePath = [self _referenceFilePathForSelector:selector identifier:identifier]; - NSData *pngData = UIImagePNGRepresentation(image); - if (nil != pngData) { - NSError *creationError = nil; - BOOL didCreateDir = [_fileManager createDirectoryAtPath:[filePath stringByDeletingLastPathComponent] - withIntermediateDirectories:YES - attributes:nil - error:&creationError]; - if (!didCreateDir) { - if (NULL != errorPtr) { - *errorPtr = creationError; - } - return NO; - } - didWrite = [pngData writeToFile:filePath options:NSDataWritingAtomic error:errorPtr]; - if (didWrite) { - NSLog(@"Reference image save at: %@", filePath); - } - } else { - if (nil != errorPtr) { - *errorPtr = [NSError errorWithDomain:FBSnapshotTestControllerErrorDomain - code:FBSnapshotTestControllerErrorCodePNGCreationFailed - userInfo:@{ - FBReferenceImageFilePathKey: filePath, - }]; - } - } - } - return didWrite; -} - -- (UIImage *)_imageForViewOrLayer:(id)viewOrLayer -{ - if ([viewOrLayer isKindOfClass:[UIView class]]) { - if (_usesDrawViewHierarchyInRect) { - return [UIImage fb_imageForView:viewOrLayer]; - } else { - return [UIImage fb_imageForViewLayer:viewOrLayer]; - } - } else if ([viewOrLayer isKindOfClass:[CALayer class]]) { - return [UIImage fb_imageForLayer:viewOrLayer]; - } else { - [NSException raise:@"Only UIView and CALayer classes can be snapshotted" format:@"%@", viewOrLayer]; - } - return nil; -} - -@end diff --git a/Example/Pods/FBSnapshotTestCase/FBSnapshotTestCase/SwiftSupport.swift b/Example/Pods/FBSnapshotTestCase/FBSnapshotTestCase/SwiftSupport.swift deleted file mode 100644 index d3058fb..0000000 --- a/Example/Pods/FBSnapshotTestCase/FBSnapshotTestCase/SwiftSupport.swift +++ /dev/null @@ -1,66 +0,0 @@ -/* -* Copyright (c) 2015, Facebook, Inc. -* All rights reserved. -* -* This source code is licensed under the BSD-style license found in the -* LICENSE file in the root directory of this source tree. An additional grant -* of patent rights can be found in the PATENTS file in the same directory. -* -*/ - -public extension FBSnapshotTestCase { - public func FBSnapshotVerifyView(view: UIView, identifier: String = "", suffixes: NSOrderedSet = FBSnapshotTestCaseDefaultSuffixes(), file: String = __FILE__, line: UInt = __LINE__) { - FBSnapshotVerifyViewOrLayer(view, identifier: identifier, suffixes: suffixes) - } - - public func FBSnapshotVerifyLayer(layer: CALayer, identifier: String = "", suffixes: NSOrderedSet = FBSnapshotTestCaseDefaultSuffixes(), file: String = __FILE__, line: UInt = __LINE__) { - FBSnapshotVerifyViewOrLayer(layer, identifier: identifier, suffixes: suffixes) - } - - private func FBSnapshotVerifyViewOrLayer(viewOrLayer: AnyObject, identifier: String = "", suffixes: NSOrderedSet = FBSnapshotTestCaseDefaultSuffixes(), file: String = __FILE__, line: UInt = __LINE__) { - let envReferenceImageDirectory = self.getReferenceImageDirectoryWithDefault(FB_REFERENCE_IMAGE_DIR) - var error: NSError? - var comparisonSuccess = false - - if let envReferenceImageDirectory = envReferenceImageDirectory { - for suffix in suffixes { - let referenceImagesDirectory = "\(envReferenceImageDirectory)\(suffix)" - if viewOrLayer.isKindOfClass(UIView) { - do { - try compareSnapshotOfView(viewOrLayer as! UIView, referenceImagesDirectory: referenceImagesDirectory, identifier: identifier, tolerance: 0) - comparisonSuccess = true - } catch let error1 as NSError { - error = error1 - comparisonSuccess = false - } - } else if viewOrLayer.isKindOfClass(CALayer) { - do { - try compareSnapshotOfLayer(viewOrLayer as! CALayer, referenceImagesDirectory: referenceImagesDirectory, identifier: identifier, tolerance: 0) - comparisonSuccess = true - } catch let error1 as NSError { - error = error1 - comparisonSuccess = false - } - } else { - assertionFailure("Only UIView and CALayer classes can be snapshotted") - } - - assert(recordMode == false, message: "Test ran in record mode. Reference image is now saved. Disable record mode to perform an actual snapshot comparison!", file: file, line: line) - - if comparisonSuccess || recordMode { - break - } - - assert(comparisonSuccess, message: "Snapshot comparison failed: \(error)", file: file, line: line) - } - } else { - XCTFail("Missing value for referenceImagesDirectory - Set FB_REFERENCE_IMAGE_DIR as Environment variable in your scheme.") - } - } - - func assert(assertion: Bool, message: String, file: String, line: UInt) { - if !assertion { - XCTFail(message, file: file, line: line) - } - } -} diff --git a/Example/Pods/FBSnapshotTestCase/LICENSE b/Example/Pods/FBSnapshotTestCase/LICENSE deleted file mode 100644 index 2dd780c..0000000 --- a/Example/Pods/FBSnapshotTestCase/LICENSE +++ /dev/null @@ -1,29 +0,0 @@ -BSD License - -For the FBSnapshotTestCase software - -Copyright (c) 2013, Facebook, Inc. -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - * Neither the name Facebook nor the names of its contributors may be used to - endorse or promote products derived from this software without specific - prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Example/Pods/FBSnapshotTestCase/README.md b/Example/Pods/FBSnapshotTestCase/README.md deleted file mode 100644 index bc23b83..0000000 --- a/Example/Pods/FBSnapshotTestCase/README.md +++ /dev/null @@ -1,97 +0,0 @@ -FBSnapshotTestCase -====================== - -[![Build Status](https://travis-ci.org/facebook/ios-snapshot-test-case.svg)](https://travis-ci.org/facebook/ios-snapshot-test-case) [![Cocoa Pod Version](https://cocoapod-badges.herokuapp.com/v/FBSnapshotTestCase/badge.svg)](http://cocoadocs.org/docsets/FBSnapshotTestCase/) - -What it does ------------- - -A "snapshot test case" takes a configured `UIView` or `CALayer` and uses the -`renderInContext:` method to get an image snapshot of its contents. It -compares this snapshot to a "reference image" stored in your source code -repository and fails the test if the two images don't match. - -Why? ----- - -At Facebook we write a lot of UI code. As you might imagine, each type of -feed story is rendered using a subclass of `UIView`. There are a lot of edge -cases that we want to handle correctly: - -- What if there is more text than can fit in the space available? -- What if an image doesn't match the size of an image view? -- What should the highlighted state look like? - -It's straightforward to test logic code, but less obvious how you should test -views. You can do a lot of rectangle asserts, but these are hard to understand -or visualize. Looking at an image diff shows you exactly what changed and how -it will look to users. - -We developed `FBSnapshotTestCase` to make snapshot tests easy. - -Installation with CocoaPods ---------------------------- - -1. Add the following lines to your Podfile: - - ``` - target "Tests" do - pod 'FBSnapshotTestCase' - end - ``` - - If you support iOS 7 use `FBSnapshotTestCase/Core` instead, which doesn't contain Swift support. - - Replace "Tests" with the name of your test project. - -2. There are [three ways](https://github.com/facebook/ios-snapshot-test-case/blob/master/FBSnapshotTestCase/FBSnapshotTestCase.h#L19-L29) of setting reference image directories, the recommended one is to define `FB_REFERENCE_IMAGE_DIR` in your scheme. This should point to the directory where you want reference images to be stored. At Facebook, we normally use this: - -|Name|Value| -|:---|:----| -|`FB_REFERENCE_IMAGE_DIR`|`$(SOURCE_ROOT)/$(PROJECT_NAME)Tests/ReferenceImages`| - - -![](FBSnapshotTestCaseDemo/Scheme_FB_REFERENCE_IMAGE_DIR.png) - -Creating a snapshot test ------------------------- - -1. Subclass `FBSnapshotTestCase` instead of `XCTestCase`. -2. From within your test, use `FBSnapshotVerifyView`. -3. Run the test once with `self.recordMode = YES;` in the test's `-setUp` - method. (This creates the reference images on disk.) -4. Remove the line enabling record mode and run the test. - -Features --------- - -- Automatically names reference images on disk according to test class and - selector. -- Prints a descriptive error message to the console on failure. (Bonus: - failure message includes a one-line command to see an image diff if - you have [Kaleidoscope](http://www.kaleidoscopeapp.com) installed.) -- Supply an optional "identifier" if you want to perform multiple snapshots - in a single test method. -- Support for `CALayer` via `FBSnapshotVerifyLayer`. -- `usesDrawViewHierarchyInRect` to handle cases like `UIVisualEffect`, `UIAppearance` and Size Classes. -- `isDeviceAgnostic` to allow appending the device model (`iPhone`, `iPad`, `iPod Touch`, etc), OS version and screen size to the images (allowing to have multiple tests for the same «snapshot» for different `OS`s and devices). - -Notes ------ - -Your unit test must be an "application test", not a "logic test." (That is, it -must be run within the Simulator so that it has access to UIKit.) In Xcode 5 -and later new projects only offer application tests, but older projects will -have separate targets for the two types. - -Authors -------- - -`FBSnapshotTestCase` was written at Facebook by -[Jonathan Dann](https://facebook.com/j.p.dann) with significant contributions by -[Todd Krabach](https://facebook.com/toddkrabach). - -License -------- - -`FBSnapshotTestCase` is BSD-licensed. See `LICENSE`. diff --git a/Example/Pods/Headers/Private/FBSnapshotTestCase/FBSnapshotTestCase.h b/Example/Pods/Headers/Private/FBSnapshotTestCase/FBSnapshotTestCase.h deleted file mode 120000 index 2925eab..0000000 --- a/Example/Pods/Headers/Private/FBSnapshotTestCase/FBSnapshotTestCase.h +++ /dev/null @@ -1 +0,0 @@ -../../../FBSnapshotTestCase/FBSnapshotTestCase/FBSnapshotTestCase.h \ No newline at end of file diff --git a/Example/Pods/Headers/Private/FBSnapshotTestCase/FBSnapshotTestCasePlatform.h b/Example/Pods/Headers/Private/FBSnapshotTestCase/FBSnapshotTestCasePlatform.h deleted file mode 120000 index 6127a30..0000000 --- a/Example/Pods/Headers/Private/FBSnapshotTestCase/FBSnapshotTestCasePlatform.h +++ /dev/null @@ -1 +0,0 @@ -../../../FBSnapshotTestCase/FBSnapshotTestCase/FBSnapshotTestCasePlatform.h \ No newline at end of file diff --git a/Example/Pods/Headers/Private/FBSnapshotTestCase/FBSnapshotTestController.h b/Example/Pods/Headers/Private/FBSnapshotTestCase/FBSnapshotTestController.h deleted file mode 120000 index 4a7dea1..0000000 --- a/Example/Pods/Headers/Private/FBSnapshotTestCase/FBSnapshotTestController.h +++ /dev/null @@ -1 +0,0 @@ -../../../FBSnapshotTestCase/FBSnapshotTestCase/FBSnapshotTestController.h \ No newline at end of file diff --git a/Example/Pods/Headers/Private/FBSnapshotTestCase/UIImage+Compare.h b/Example/Pods/Headers/Private/FBSnapshotTestCase/UIImage+Compare.h deleted file mode 120000 index 2fd266d..0000000 --- a/Example/Pods/Headers/Private/FBSnapshotTestCase/UIImage+Compare.h +++ /dev/null @@ -1 +0,0 @@ -../../../FBSnapshotTestCase/FBSnapshotTestCase/Categories/UIImage+Compare.h \ No newline at end of file diff --git a/Example/Pods/Headers/Private/FBSnapshotTestCase/UIImage+Diff.h b/Example/Pods/Headers/Private/FBSnapshotTestCase/UIImage+Diff.h deleted file mode 120000 index 2ecee67..0000000 --- a/Example/Pods/Headers/Private/FBSnapshotTestCase/UIImage+Diff.h +++ /dev/null @@ -1 +0,0 @@ -../../../FBSnapshotTestCase/FBSnapshotTestCase/Categories/UIImage+Diff.h \ No newline at end of file diff --git a/Example/Pods/Headers/Private/FBSnapshotTestCase/UIImage+Snapshot.h b/Example/Pods/Headers/Private/FBSnapshotTestCase/UIImage+Snapshot.h deleted file mode 120000 index 577a4cb..0000000 --- a/Example/Pods/Headers/Private/FBSnapshotTestCase/UIImage+Snapshot.h +++ /dev/null @@ -1 +0,0 @@ -../../../FBSnapshotTestCase/FBSnapshotTestCase/Categories/UIImage+Snapshot.h \ No newline at end of file diff --git a/Example/Pods/Local Podspecs/Franz.podspec.json b/Example/Pods/Local Podspecs/Franz.podspec.json deleted file mode 100644 index c2cf0ad..0000000 --- a/Example/Pods/Local Podspecs/Franz.podspec.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "name": "Franz", - "version": "0.1.0", - "summary": "A short description of Franz.", - "description": "", - "homepage": "https://github.com/kellanburket/Franz", - "license": "MIT", - "authors": { - "kellanburket": "kellan.burket@gmail.com" - }, - "source": { - "git": "https://github.com//Franz.git", - "tag": "0.1.0" - }, - "requires_arc": true, - "source_files": "Pod/Classes/**/*", - "resource_bundles": { - "Franz": [ - "Pod/Assets/*.png" - ] - } -} diff --git a/Example/Pods/Manifest.lock b/Example/Pods/Manifest.lock deleted file mode 100644 index 3236575..0000000 --- a/Example/Pods/Manifest.lock +++ /dev/null @@ -1,21 +0,0 @@ -PODS: - - FBSnapshotTestCase (2.0.7): - - FBSnapshotTestCase/SwiftSupport (= 2.0.7) - - FBSnapshotTestCase/Core (2.0.7) - - FBSnapshotTestCase/SwiftSupport (2.0.7): - - FBSnapshotTestCase/Core - - Franz (0.1.0) - -DEPENDENCIES: - - FBSnapshotTestCase - - Franz (from `../`) - -EXTERNAL SOURCES: - Franz: - :path: "../" - -SPEC CHECKSUMS: - FBSnapshotTestCase: 7e85180d0d141a0cf472352edda7e80d7eaeb547 - Franz: 1cb521121a59a93d69f1c3466f0ac6b921f31f8b - -COCOAPODS: 0.39.0 diff --git a/Example/Pods/Pods.xcodeproj/project.pbxproj b/Example/Pods/Pods.xcodeproj/project.pbxproj deleted file mode 100644 index 996a85a..0000000 --- a/Example/Pods/Pods.xcodeproj/project.pbxproj +++ /dev/null @@ -1,1122 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 46; - objects = { - -/* Begin PBXBuildFile section */ - 03B775E406BEB00CA5AD3ACA399A21C5 /* Partition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02EB238180060CCF49CCB44C4E91190D /* Partition.swift */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; - 0F8E3DF1028C43BEF58686F910DA51A9 /* FetchAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5261B2AD9749A27F206204FA6C9C838D /* FetchAPI.swift */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; - 163C6E8E5E7CEE1E0BEEB9CA0939DA3A /* UIImage+Diff.m in Sources */ = {isa = PBXBuildFile; fileRef = 73003A0752F3CABAA497D1FD1F5E4E44 /* UIImage+Diff.m */; }; - 172C65BF2DCBA1125B110CC888EEF1D9 /* Pods-Franz_Example-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = CFC63EEFFEE3FB7A058994D37563E916 /* Pods-Franz_Example-dummy.m */; }; - 1F118728B3D74089480152A6B660EA9A /* UIImage+Compare.h in Headers */ = {isa = PBXBuildFile; fileRef = A729E2AD854B4CEB4EF52289C017EB3B /* UIImage+Compare.h */; settings = {ATTRIBUTES = (Private, ); }; }; - 23A955053E0AED5359F6F5F07E80DB97 /* KafkaProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2BF7A20E4B038E5BA8571E79EDDE06D1 /* KafkaProtocol.swift */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; - 23ABCF90EFE58D6A3C695FDF5CBAC606 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D31B096AB4A0164C61B63F0E59AD76C9 /* Foundation.framework */; }; - 257ED6B3AACF80A75600C6851F4740F9 /* MessageSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7D2A38CCBF3E7D1F2F6DE754F58D865 /* MessageSet.swift */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; - 28F2071FFF5C5B0E9DEB6C3FE43A30CC /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2393D1A899C707F79A64672C46ED238F /* UIKit.framework */; }; - 2E4C870FB0C0C9B1BC1B2ECC09B9DFAF /* Connection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C37831BCBA63E346F8F08F34ABC98F4 /* Connection.swift */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; - 35848D70EAC9237246BDF63457729694 /* FBSnapshotTestCase.m in Sources */ = {isa = PBXBuildFile; fileRef = 2523693A8A7BA4278BFC866F16021FFF /* FBSnapshotTestCase.m */; }; - 41D0C3EF5AC6F1E2CDA22DD7E4A43AE5 /* MetadataAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E72BEB8816B62053B902BF9053C97F6 /* MetadataAPI.swift */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; - 41D8739E2720D3535592391C25507158 /* FBSnapshotTestCasePlatform.h in Headers */ = {isa = PBXBuildFile; fileRef = 12CC4BD9C5F6D514C917D7942DF2D2F4 /* FBSnapshotTestCasePlatform.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 446491C54EF0600A87F86D39B2A506A4 /* Broker.swift in Sources */ = {isa = PBXBuildFile; fileRef = E59F010BD1EDFB7032F9E0DD90170356 /* Broker.swift */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; - 52B0263F90696E7FD87CB146D5C5E2C1 /* UIImage+Diff.h in Headers */ = {isa = PBXBuildFile; fileRef = 7CAAAB29C54DB2D5220028C4110E45E6 /* UIImage+Diff.h */; settings = {ATTRIBUTES = (Private, ); }; }; - 52F7A46AB0053D8BB1D793B917D37BB2 /* SwiftSupport.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDE5208FE4A05C9114818BC72105E5F7 /* SwiftSupport.swift */; }; - 59F2FB04A091A000E6F8153C8E60FE49 /* GroupMembershipAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A85C5E3C65B0C2FE8B6D550A28825FC /* GroupMembershipAPI.swift */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; - 5D14695A69EC4A8DF7488D0FC59C105C /* FBSnapshotTestCasePlatform.m in Sources */ = {isa = PBXBuildFile; fileRef = E38041D93EDE9398D62F150F8F25DE47 /* FBSnapshotTestCasePlatform.m */; }; - 6020279138D17C4AE462EAA10E244430 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D31B096AB4A0164C61B63F0E59AD76C9 /* Foundation.framework */; }; - 61EF6F029586143469BBBA49D82646C6 /* AdminAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2E26731DB888D4C460484424A2ED651F /* AdminAPI.swift */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; - 684B72C095A6F539D321750072B6995F /* GroupCoordinatorAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2DC1F19EF2384984CFBFB0902F27D63 /* GroupCoordinatorAPI.swift */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; - 723FDDFD804188090511DEFA8309A06B /* UIImage+Snapshot.h in Headers */ = {isa = PBXBuildFile; fileRef = 0345D737618C6A063BD152389ACCBF87 /* UIImage+Snapshot.h */; settings = {ATTRIBUTES = (Private, ); }; }; - 76299E1B1C55882900C4EDE1 /* Cluster.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A812AF34C9481DB730691CAAAC731A4 /* Cluster.swift */; }; - 76333B141C57CA6A00556A70 /* Group.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76333B121C57C9D300556A70 /* Group.swift */; }; - 763720C31C64F67A0028E009 /* Consumer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 763720C11C64F52E0028E009 /* Consumer.swift */; }; - 76F973731C5ECA430084C4A0 /* OffsetFetchAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76F973711C5ECA250084C4A0 /* OffsetFetchAPI.swift */; }; - 788E3D9458FE9540469981BE9A0B227C /* UIImage+Compare.m in Sources */ = {isa = PBXBuildFile; fileRef = 5999002674B86E70DC7970CEA62F278E /* UIImage+Compare.m */; }; - 7BEF20E09F0E864478905311672F91AB /* FBSnapshotTestController.h in Headers */ = {isa = PBXBuildFile; fileRef = 4AC2C5814DCFC7590AC8C46AAB7FCEEF /* FBSnapshotTestController.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 80C79D920A1B124F0E5C1EE749E83598 /* OffsetCommitAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CBFE487B6497221DA7FC0DC25C03032 /* OffsetCommitAPI.swift */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; - 821A26883ED14BDCD7EBADD37D671D17 /* FBSnapshotTestCase.h in Headers */ = {isa = PBXBuildFile; fileRef = 0EE4692BF9A76DAAA67A8FF4DABD27E8 /* FBSnapshotTestCase.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 82DDF40C496D87C0A83C6DF039E0481E /* OffsetAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA9EF77B3DAFCF252E7A17BD71D32852 /* OffsetAPI.swift */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; - 8384DFD78FD8BF8A6D04D27FA56AB554 /* KafkaResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = B96B360AE51EFDE67C733F9BBBDEFB0D /* KafkaResponse.swift */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; - 8D549E01DA1D5104335CCEABFF870005 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D31B096AB4A0164C61B63F0E59AD76C9 /* Foundation.framework */; }; - A3512174664A2DE37A684024116E9CD4 /* Pods-Franz_Tests-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 2FF178A2DF7E54B947043967492084F7 /* Pods-Franz_Tests-dummy.m */; }; - A6398C850DD9F9A8EC4D386F27C79A73 /* CRC32.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DE7D67AFF967F45816DF30F53947EF0 /* CRC32.swift */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; - AA8501741ABB8D75C6C47DE086BACD37 /* FBSnapshotTestController.m in Sources */ = {isa = PBXBuildFile; fileRef = BFFD97972C24EE7435D0E04686569BBE /* FBSnapshotTestController.m */; }; - AD4D905241DED4AEAD67447088ACB23D /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 853C84AC883B6FC690E82525E7ECB377 /* XCTest.framework */; }; - B0CC969880D86C3CFB7EDFF61E19BEED /* Pods-Franz_Tests-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = DA1068208BCAB0A25EE278D6C7AE1845 /* Pods-Franz_Tests-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; - BBE11E9AA03E61B5011FD3399011073B /* Variables.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73B6FFCD1F520B52B559B1DD6C6ECF0 /* Variables.swift */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; - BF1A026373E7F73D751AC8C51262B45A /* Franz.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 1A97A309EC8D813C2E6B909E3E845945 /* Franz.bundle */; }; - C1A4ABF392DBF83F0A85681383C8B1B7 /* Franz-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = C7101CDBB09AB904EA9248B0F8ABA417 /* Franz-dummy.m */; }; - CC2BD0A04199B280AD93F459968CAFC2 /* FBSnapshotTestCase-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = DF90F95FC2B36AB64D2FB3AF22E996D2 /* FBSnapshotTestCase-dummy.m */; }; - CF9DB2096C715EA61E263021EE406DB1 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0528444EFC68619AD1FEB8A7CD4A01BA /* QuartzCore.framework */; }; - D2ED6D646165FD1C08135F12BB20A00D /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D31B096AB4A0164C61B63F0E59AD76C9 /* Foundation.framework */; }; - D3CCFCE4A651A0EF30BF8BEBB838486B /* ProduceAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9C783899B37D8C839A02861DED3AE879 /* ProduceAPI.swift */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; - D975B6057CFFA2A9E09E9C87D0642283 /* Enumerations.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9CEAB86465FAB6EB1C61C977A446A64 /* Enumerations.swift */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; - E7FCA6609F3B9CBD9037D88DB504A178 /* Wildcard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9C993C91C4F504A8E0A0CF61847C6FC8 /* Wildcard.swift */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; - E8F83144CB60E17DC4FCE06AC4779AE3 /* Topic.swift in Sources */ = {isa = PBXBuildFile; fileRef = 233B337B3C42318773C9F565D391FD55 /* Topic.swift */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; - EC21143E6EE40EA12FAE77C2E9826291 /* Franz-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = B4EF0845393103A457EB0D73471210C1 /* Franz-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; - ED318AD42CDAB08D7802766CB82216B8 /* KafkaRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3432352D59985E862814071909BCFD9C /* KafkaRequest.swift */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; - EDD91D0CC316AC322E1607D3749F1C0A /* UIImage+Snapshot.m in Sources */ = {isa = PBXBuildFile; fileRef = 5121BCE99CCFB208275F3437CCF6EBB6 /* UIImage+Snapshot.m */; }; - F100146FF21AAB21AC21DE751BB3B26C /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF06610F1037EE2C7D3F76B2FF6CC258 /* Extensions.swift */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; - FFC5E408CFCB9810D3387E85E15A9A7D /* Pods-Franz_Example-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = BC4E5D0A39115D31E278FB552282CE18 /* Pods-Franz_Example-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; -/* End PBXBuildFile section */ - -/* Begin PBXContainerItemProxy section */ - 560B5DD9E7E5DEA38DCE05CF62E19820 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; - proxyType = 1; - remoteGlobalIDString = 2BF12414DBFE95956DBB5292A7BA6841; - remoteInfo = Franz; - }; - 6B3E59F5137FB43DD433E604E9B995B1 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; - proxyType = 1; - remoteGlobalIDString = 2BF12414DBFE95956DBB5292A7BA6841; - remoteInfo = Franz; - }; - 72F8C95D640755A36BE98853640B49D8 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; - proxyType = 1; - remoteGlobalIDString = 709EBC98230D3C4D1C634D4E5381A3DD; - remoteInfo = "Franz-Franz"; - }; - DD2894197F3509352A1F7F92F71A57BB /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; - proxyType = 1; - remoteGlobalIDString = A5C935B614DD08342117AD0B06959CB9; - remoteInfo = FBSnapshotTestCase; - }; -/* End PBXContainerItemProxy section */ - -/* Begin PBXFileReference section */ - 02EB238180060CCF49CCB44C4E91190D /* Partition.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = Partition.swift; sourceTree = ""; }; - 0345D737618C6A063BD152389ACCBF87 /* UIImage+Snapshot.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIImage+Snapshot.h"; path = "FBSnapshotTestCase/Categories/UIImage+Snapshot.h"; sourceTree = ""; }; - 0528444EFC68619AD1FEB8A7CD4A01BA /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS9.0.sdk/System/Library/Frameworks/QuartzCore.framework; sourceTree = DEVELOPER_DIR; }; - 0A812AF34C9481DB730691CAAAC731A4 /* Cluster.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = Cluster.swift; sourceTree = ""; }; - 0C4D78298306D849DA1A0600947F964E /* Pods-Franz_Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-Franz_Example.debug.xcconfig"; sourceTree = ""; }; - 0EE4692BF9A76DAAA67A8FF4DABD27E8 /* FBSnapshotTestCase.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FBSnapshotTestCase.h; path = FBSnapshotTestCase/FBSnapshotTestCase.h; sourceTree = ""; }; - 0F01AA0F45B0B5CC535F9884FF44262A /* FBSnapshotTestCase.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = "sourcecode.module-map"; path = FBSnapshotTestCase.modulemap; sourceTree = ""; }; - 0F52E2F10DA737BFFF877A65E4FCB863 /* Pods-Franz_Tests.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = "sourcecode.module-map"; path = "Pods-Franz_Tests.modulemap"; sourceTree = ""; }; - 1171FF23BCFC871C7B11676BF56F6962 /* Pods-Franz_Tests-frameworks.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-Franz_Tests-frameworks.sh"; sourceTree = ""; }; - 12CC4BD9C5F6D514C917D7942DF2D2F4 /* FBSnapshotTestCasePlatform.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FBSnapshotTestCasePlatform.h; path = FBSnapshotTestCase/FBSnapshotTestCasePlatform.h; sourceTree = ""; }; - 1A97A309EC8D813C2E6B909E3E845945 /* Franz.bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Franz.bundle; sourceTree = BUILT_PRODUCTS_DIR; }; - 233B337B3C42318773C9F565D391FD55 /* Topic.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = Topic.swift; sourceTree = ""; }; - 2393D1A899C707F79A64672C46ED238F /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS9.0.sdk/System/Library/Frameworks/UIKit.framework; sourceTree = DEVELOPER_DIR; }; - 2523693A8A7BA4278BFC866F16021FFF /* FBSnapshotTestCase.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FBSnapshotTestCase.m; path = FBSnapshotTestCase/FBSnapshotTestCase.m; sourceTree = ""; }; - 2BF7A20E4B038E5BA8571E79EDDE06D1 /* KafkaProtocol.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = KafkaProtocol.swift; sourceTree = ""; }; - 2DE7D67AFF967F45816DF30F53947EF0 /* CRC32.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = CRC32.swift; sourceTree = ""; }; - 2E26731DB888D4C460484424A2ED651F /* AdminAPI.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = AdminAPI.swift; sourceTree = ""; }; - 2FF178A2DF7E54B947043967492084F7 /* Pods-Franz_Tests-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-Franz_Tests-dummy.m"; sourceTree = ""; }; - 305DFCEC761D51CE1E2B01BD8B083191 /* Franz-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Franz-prefix.pch"; sourceTree = ""; }; - 3432352D59985E862814071909BCFD9C /* KafkaRequest.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = KafkaRequest.swift; sourceTree = ""; }; - 3CDD9962E7C6687DEEC40FC2F5F6DC17 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 42A681D3AF997235908A3089BB9BCB20 /* FBSnapshotTestCase.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = FBSnapshotTestCase.xcconfig; sourceTree = ""; }; - 44861F6BC76277A5D6151358E24D83C7 /* Pods-Franz_Example-frameworks.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-Franz_Example-frameworks.sh"; sourceTree = ""; }; - 4AC2C5814DCFC7590AC8C46AAB7FCEEF /* FBSnapshotTestController.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FBSnapshotTestController.h; path = FBSnapshotTestCase/FBSnapshotTestController.h; sourceTree = ""; }; - 4C37831BCBA63E346F8F08F34ABC98F4 /* Connection.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = Connection.swift; sourceTree = ""; }; - 4D0E693D2F6C3421F514E4E35486D03A /* Pods-Franz_Example-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-Franz_Example-acknowledgements.plist"; sourceTree = ""; }; - 4D966773B36EAD90E3A2E9745AEB71FB /* Pods-Franz_Tests-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-Franz_Tests-acknowledgements.markdown"; sourceTree = ""; }; - 4E94E3A8DCCC076AA2D1738F42D892C7 /* Pods-Franz_Tests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-Franz_Tests.release.xcconfig"; sourceTree = ""; }; - 50790180608908F39079564536CA8C59 /* Pods-Franz_Tests-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-Franz_Tests-acknowledgements.plist"; sourceTree = ""; }; - 507F6F7DD3B262AE27DF459A69448716 /* FBSnapshotTestCase.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = FBSnapshotTestCase.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 5121BCE99CCFB208275F3437CCF6EBB6 /* UIImage+Snapshot.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIImage+Snapshot.m"; path = "FBSnapshotTestCase/Categories/UIImage+Snapshot.m"; sourceTree = ""; }; - 5261B2AD9749A27F206204FA6C9C838D /* FetchAPI.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = FetchAPI.swift; sourceTree = ""; }; - 5999002674B86E70DC7970CEA62F278E /* UIImage+Compare.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIImage+Compare.m"; path = "FBSnapshotTestCase/Categories/UIImage+Compare.m"; sourceTree = ""; }; - 5AA71D6414ACB25B984EE048BD9EF44F /* Franz.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = "sourcecode.module-map"; path = Franz.modulemap; sourceTree = ""; }; - 5BEEC2AAA68DF2A94D30301C90B435E0 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 5E72BEB8816B62053B902BF9053C97F6 /* MetadataAPI.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = MetadataAPI.swift; sourceTree = ""; }; - 6CBFE487B6497221DA7FC0DC25C03032 /* OffsetCommitAPI.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = OffsetCommitAPI.swift; sourceTree = ""; }; - 71B05C8E3C6D92BDFE8AD10416A2C201 /* Pods-Franz_Tests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-Franz_Tests.debug.xcconfig"; sourceTree = ""; }; - 73003A0752F3CABAA497D1FD1F5E4E44 /* UIImage+Diff.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIImage+Diff.m"; path = "FBSnapshotTestCase/Categories/UIImage+Diff.m"; sourceTree = ""; }; - 76333B121C57C9D300556A70 /* Group.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Group.swift; sourceTree = ""; }; - 763720C11C64F52E0028E009 /* Consumer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Consumer.swift; sourceTree = ""; }; - 76F973711C5ECA250084C4A0 /* OffsetFetchAPI.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OffsetFetchAPI.swift; sourceTree = ""; }; - 7BAF34E92C81EABA6A308F8F749080A1 /* Pods-Franz_Example.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = "sourcecode.module-map"; path = "Pods-Franz_Example.modulemap"; sourceTree = ""; }; - 7CAAAB29C54DB2D5220028C4110E45E6 /* UIImage+Diff.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIImage+Diff.h"; path = "FBSnapshotTestCase/Categories/UIImage+Diff.h"; sourceTree = ""; }; - 853C84AC883B6FC690E82525E7ECB377 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS9.0.sdk/System/Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; - 9A85C5E3C65B0C2FE8B6D550A28825FC /* GroupMembershipAPI.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = GroupMembershipAPI.swift; sourceTree = ""; }; - 9C783899B37D8C839A02861DED3AE879 /* ProduceAPI.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ProduceAPI.swift; sourceTree = ""; }; - 9C993C91C4F504A8E0A0CF61847C6FC8 /* Wildcard.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = Wildcard.swift; sourceTree = ""; }; - A729E2AD854B4CEB4EF52289C017EB3B /* UIImage+Compare.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIImage+Compare.h"; path = "FBSnapshotTestCase/Categories/UIImage+Compare.h"; sourceTree = ""; }; - A7D2A38CCBF3E7D1F2F6DE754F58D865 /* MessageSet.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = MessageSet.swift; sourceTree = ""; }; - B3D08C65AB18ACF0D05C7C79DB5737E4 /* FBSnapshotTestCase-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "FBSnapshotTestCase-prefix.pch"; sourceTree = ""; }; - B4EF0845393103A457EB0D73471210C1 /* Franz-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Franz-umbrella.h"; sourceTree = ""; }; - B6987A2655B67D1203F06CEA0E30EA57 /* Pods-Franz_Example-resources.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-Franz_Example-resources.sh"; sourceTree = ""; }; - B8EB56319AF316402CD1A84039B560F7 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - B96B360AE51EFDE67C733F9BBBDEFB0D /* KafkaResponse.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = KafkaResponse.swift; sourceTree = ""; }; - B9CEAB86465FAB6EB1C61C977A446A64 /* Enumerations.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = Enumerations.swift; sourceTree = ""; }; - BA6428E9F66FD5A23C0A2E06ED26CD2F /* Podfile */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = Podfile; path = ../Podfile; sourceTree = SOURCE_ROOT; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; - BC4E5D0A39115D31E278FB552282CE18 /* Pods-Franz_Example-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Pods-Franz_Example-umbrella.h"; sourceTree = ""; }; - BF9E6087E228BDD1D7A55D21DD2C8EB4 /* Pods-Franz_Example-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-Franz_Example-acknowledgements.markdown"; sourceTree = ""; }; - BFFD97972C24EE7435D0E04686569BBE /* FBSnapshotTestController.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FBSnapshotTestController.m; path = FBSnapshotTestCase/FBSnapshotTestController.m; sourceTree = ""; }; - C7101CDBB09AB904EA9248B0F8ABA417 /* Franz-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Franz-dummy.m"; sourceTree = ""; }; - CA9EF77B3DAFCF252E7A17BD71D32852 /* OffsetAPI.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = OffsetAPI.swift; sourceTree = ""; }; - CC405BC7C47C945D28D073DA88888B85 /* Pods_Franz_Tests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Franz_Tests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - CFC63EEFFEE3FB7A058994D37563E916 /* Pods-Franz_Example-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-Franz_Example-dummy.m"; sourceTree = ""; }; - D10243C43EFCDCC79DA4713F236FFF2C /* Franz.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Franz.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - D31B096AB4A0164C61B63F0E59AD76C9 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS9.0.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; }; - D5D2BB319BBC1D08C8C175651E9DCEC0 /* Pods-Franz_Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-Franz_Example.release.xcconfig"; sourceTree = ""; }; - D73B6FFCD1F520B52B559B1DD6C6ECF0 /* Variables.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = Variables.swift; sourceTree = ""; }; - DA1068208BCAB0A25EE278D6C7AE1845 /* Pods-Franz_Tests-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Pods-Franz_Tests-umbrella.h"; sourceTree = ""; }; - DCFFF2B8D3CB8B7492CA5A91A2A8AEBC /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - DF90F95FC2B36AB64D2FB3AF22E996D2 /* FBSnapshotTestCase-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "FBSnapshotTestCase-dummy.m"; sourceTree = ""; }; - E1E9747F81C3C97E561519935AEFBA1B /* Pods-Franz_Tests-resources.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-Franz_Tests-resources.sh"; sourceTree = ""; }; - E2DC1F19EF2384984CFBFB0902F27D63 /* GroupCoordinatorAPI.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = GroupCoordinatorAPI.swift; sourceTree = ""; }; - E38041D93EDE9398D62F150F8F25DE47 /* FBSnapshotTestCasePlatform.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FBSnapshotTestCasePlatform.m; path = FBSnapshotTestCase/FBSnapshotTestCasePlatform.m; sourceTree = ""; }; - E59F010BD1EDFB7032F9E0DD90170356 /* Broker.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = Broker.swift; sourceTree = ""; }; - EEFA8105BF0B1AD7F976E6F7FDB3F1F2 /* Pods_Franz_Example.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Franz_Example.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - FDE5208FE4A05C9114818BC72105E5F7 /* SwiftSupport.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SwiftSupport.swift; path = FBSnapshotTestCase/SwiftSupport.swift; sourceTree = ""; }; - FF06610F1037EE2C7D3F76B2FF6CC258 /* Extensions.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = Extensions.swift; sourceTree = ""; }; - FF1DE5A1E98A0C5720684604D9FCD86D /* Franz.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = Franz.xcconfig; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 8091AF417F54DE791CC7AABE3620AAB1 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - D2ED6D646165FD1C08135F12BB20A00D /* Foundation.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 96317938B7BBB0DE5B6C6F27B4A6333F /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 8D549E01DA1D5104335CCEABFF870005 /* Foundation.framework in Frameworks */, - CF9DB2096C715EA61E263021EE406DB1 /* QuartzCore.framework in Frameworks */, - 28F2071FFF5C5B0E9DEB6C3FE43A30CC /* UIKit.framework in Frameworks */, - AD4D905241DED4AEAD67447088ACB23D /* XCTest.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - A8C217B3E07F1D71FBD19223E34729D1 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 23ABCF90EFE58D6A3C695FDF5CBAC606 /* Foundation.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - B6FF4273E38465E2AEE1DBAB46D55443 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - DA2C41D0623C56F80F7EB7E1C5DA1A22 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 6020279138D17C4AE462EAA10E244430 /* Foundation.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 14B8B9B15ECBE87983FF987239AB2D7B /* Frameworks */ = { - isa = PBXGroup; - children = ( - CD3FB7D37E46A5379C926728A416D78E /* iOS */, - ); - name = Frameworks; - sourceTree = ""; - }; - 2F6239A63D856C831764CB0EA603D03D /* Targets Support Files */ = { - isa = PBXGroup; - children = ( - 8FBD7E8DB8D06819212D1826A5A3F889 /* Pods-Franz_Example */, - 5C39C951600B01330A9BFCD16C4079CE /* Pods-Franz_Tests */, - ); - name = "Targets Support Files"; - sourceTree = ""; - }; - 329CC004AB83B695DB001E6BBD6F7707 /* SwiftSupport */ = { - isa = PBXGroup; - children = ( - FDE5208FE4A05C9114818BC72105E5F7 /* SwiftSupport.swift */, - ); - name = SwiftSupport; - sourceTree = ""; - }; - 5060B33D8CFD3C2E88D6F156E54E3265 /* Core */ = { - isa = PBXGroup; - children = ( - 0EE4692BF9A76DAAA67A8FF4DABD27E8 /* FBSnapshotTestCase.h */, - 2523693A8A7BA4278BFC866F16021FFF /* FBSnapshotTestCase.m */, - 12CC4BD9C5F6D514C917D7942DF2D2F4 /* FBSnapshotTestCasePlatform.h */, - E38041D93EDE9398D62F150F8F25DE47 /* FBSnapshotTestCasePlatform.m */, - 4AC2C5814DCFC7590AC8C46AAB7FCEEF /* FBSnapshotTestController.h */, - BFFD97972C24EE7435D0E04686569BBE /* FBSnapshotTestController.m */, - A729E2AD854B4CEB4EF52289C017EB3B /* UIImage+Compare.h */, - 5999002674B86E70DC7970CEA62F278E /* UIImage+Compare.m */, - 7CAAAB29C54DB2D5220028C4110E45E6 /* UIImage+Diff.h */, - 73003A0752F3CABAA497D1FD1F5E4E44 /* UIImage+Diff.m */, - 0345D737618C6A063BD152389ACCBF87 /* UIImage+Snapshot.h */, - 5121BCE99CCFB208275F3437CCF6EBB6 /* UIImage+Snapshot.m */, - ); - name = Core; - sourceTree = ""; - }; - 5C39C951600B01330A9BFCD16C4079CE /* Pods-Franz_Tests */ = { - isa = PBXGroup; - children = ( - DCFFF2B8D3CB8B7492CA5A91A2A8AEBC /* Info.plist */, - 0F52E2F10DA737BFFF877A65E4FCB863 /* Pods-Franz_Tests.modulemap */, - 4D966773B36EAD90E3A2E9745AEB71FB /* Pods-Franz_Tests-acknowledgements.markdown */, - 50790180608908F39079564536CA8C59 /* Pods-Franz_Tests-acknowledgements.plist */, - 2FF178A2DF7E54B947043967492084F7 /* Pods-Franz_Tests-dummy.m */, - 1171FF23BCFC871C7B11676BF56F6962 /* Pods-Franz_Tests-frameworks.sh */, - E1E9747F81C3C97E561519935AEFBA1B /* Pods-Franz_Tests-resources.sh */, - DA1068208BCAB0A25EE278D6C7AE1845 /* Pods-Franz_Tests-umbrella.h */, - 71B05C8E3C6D92BDFE8AD10416A2C201 /* Pods-Franz_Tests.debug.xcconfig */, - 4E94E3A8DCCC076AA2D1738F42D892C7 /* Pods-Franz_Tests.release.xcconfig */, - ); - name = "Pods-Franz_Tests"; - path = "Target Support Files/Pods-Franz_Tests"; - sourceTree = ""; - }; - 650EC9539C7C0C369EE72177C037CF38 /* Franz */ = { - isa = PBXGroup; - children = ( - 8918DDCE2A6535B37002D8F9560572E1 /* Pod */, - B42C7C7E294986841F4BCC9D720DC4C0 /* Support Files */, - ); - name = Franz; - path = ../..; - sourceTree = ""; - }; - 7A0B45E8C6E3EE49D32C3C7B20CA3181 /* Crypt */ = { - isa = PBXGroup; - children = ( - 2DE7D67AFF967F45816DF30F53947EF0 /* CRC32.swift */, - ); - path = Crypt; - sourceTree = ""; - }; - 7DB346D0F39D3F0E887471402A8071AB = { - isa = PBXGroup; - children = ( - BA6428E9F66FD5A23C0A2E06ED26CD2F /* Podfile */, - 9611C2E4551B1682704FE5649FD41495 /* Development Pods */, - 14B8B9B15ECBE87983FF987239AB2D7B /* Frameworks */, - FBD2D672921284E66F309CA3462F84D6 /* Pods */, - B20B3905D37D2F76DB408FF9398B1707 /* Products */, - 2F6239A63D856C831764CB0EA603D03D /* Targets Support Files */, - ); - sourceTree = ""; - }; - 8004835372142699707E98D241D8C23D /* FBSnapshotTestCase */ = { - isa = PBXGroup; - children = ( - 5060B33D8CFD3C2E88D6F156E54E3265 /* Core */, - A78CE87681B42105A65DA44E0F79FC82 /* Support Files */, - 329CC004AB83B695DB001E6BBD6F7707 /* SwiftSupport */, - ); - path = FBSnapshotTestCase; - sourceTree = ""; - }; - 83C42884054CA07BC478A2D2C4B467C6 /* Classes */ = { - isa = PBXGroup; - children = ( - 2E26731DB888D4C460484424A2ED651F /* AdminAPI.swift */, - 763720C11C64F52E0028E009 /* Consumer.swift */, - E59F010BD1EDFB7032F9E0DD90170356 /* Broker.swift */, - 0A812AF34C9481DB730691CAAAC731A4 /* Cluster.swift */, - 4C37831BCBA63E346F8F08F34ABC98F4 /* Connection.swift */, - B9CEAB86465FAB6EB1C61C977A446A64 /* Enumerations.swift */, - FF06610F1037EE2C7D3F76B2FF6CC258 /* Extensions.swift */, - 5261B2AD9749A27F206204FA6C9C838D /* FetchAPI.swift */, - E2DC1F19EF2384984CFBFB0902F27D63 /* GroupCoordinatorAPI.swift */, - 9A85C5E3C65B0C2FE8B6D550A28825FC /* GroupMembershipAPI.swift */, - 2BF7A20E4B038E5BA8571E79EDDE06D1 /* KafkaProtocol.swift */, - 3432352D59985E862814071909BCFD9C /* KafkaRequest.swift */, - B96B360AE51EFDE67C733F9BBBDEFB0D /* KafkaResponse.swift */, - A7D2A38CCBF3E7D1F2F6DE754F58D865 /* MessageSet.swift */, - 5E72BEB8816B62053B902BF9053C97F6 /* MetadataAPI.swift */, - CA9EF77B3DAFCF252E7A17BD71D32852 /* OffsetAPI.swift */, - 6CBFE487B6497221DA7FC0DC25C03032 /* OffsetCommitAPI.swift */, - 76F973711C5ECA250084C4A0 /* OffsetFetchAPI.swift */, - 76333B121C57C9D300556A70 /* Group.swift */, - 02EB238180060CCF49CCB44C4E91190D /* Partition.swift */, - 9C783899B37D8C839A02861DED3AE879 /* ProduceAPI.swift */, - 233B337B3C42318773C9F565D391FD55 /* Topic.swift */, - D73B6FFCD1F520B52B559B1DD6C6ECF0 /* Variables.swift */, - 9C993C91C4F504A8E0A0CF61847C6FC8 /* Wildcard.swift */, - 7A0B45E8C6E3EE49D32C3C7B20CA3181 /* Crypt */, - ); - path = Classes; - sourceTree = ""; - }; - 8918DDCE2A6535B37002D8F9560572E1 /* Pod */ = { - isa = PBXGroup; - children = ( - 83C42884054CA07BC478A2D2C4B467C6 /* Classes */, - ); - path = Pod; - sourceTree = ""; - }; - 8FBD7E8DB8D06819212D1826A5A3F889 /* Pods-Franz_Example */ = { - isa = PBXGroup; - children = ( - 5BEEC2AAA68DF2A94D30301C90B435E0 /* Info.plist */, - 7BAF34E92C81EABA6A308F8F749080A1 /* Pods-Franz_Example.modulemap */, - BF9E6087E228BDD1D7A55D21DD2C8EB4 /* Pods-Franz_Example-acknowledgements.markdown */, - 4D0E693D2F6C3421F514E4E35486D03A /* Pods-Franz_Example-acknowledgements.plist */, - CFC63EEFFEE3FB7A058994D37563E916 /* Pods-Franz_Example-dummy.m */, - 44861F6BC76277A5D6151358E24D83C7 /* Pods-Franz_Example-frameworks.sh */, - B6987A2655B67D1203F06CEA0E30EA57 /* Pods-Franz_Example-resources.sh */, - BC4E5D0A39115D31E278FB552282CE18 /* Pods-Franz_Example-umbrella.h */, - 0C4D78298306D849DA1A0600947F964E /* Pods-Franz_Example.debug.xcconfig */, - D5D2BB319BBC1D08C8C175651E9DCEC0 /* Pods-Franz_Example.release.xcconfig */, - ); - name = "Pods-Franz_Example"; - path = "Target Support Files/Pods-Franz_Example"; - sourceTree = ""; - }; - 9611C2E4551B1682704FE5649FD41495 /* Development Pods */ = { - isa = PBXGroup; - children = ( - 650EC9539C7C0C369EE72177C037CF38 /* Franz */, - ); - name = "Development Pods"; - sourceTree = ""; - }; - A78CE87681B42105A65DA44E0F79FC82 /* Support Files */ = { - isa = PBXGroup; - children = ( - 0F01AA0F45B0B5CC535F9884FF44262A /* FBSnapshotTestCase.modulemap */, - 42A681D3AF997235908A3089BB9BCB20 /* FBSnapshotTestCase.xcconfig */, - DF90F95FC2B36AB64D2FB3AF22E996D2 /* FBSnapshotTestCase-dummy.m */, - B3D08C65AB18ACF0D05C7C79DB5737E4 /* FBSnapshotTestCase-prefix.pch */, - 3CDD9962E7C6687DEEC40FC2F5F6DC17 /* Info.plist */, - ); - name = "Support Files"; - path = "../Target Support Files/FBSnapshotTestCase"; - sourceTree = ""; - }; - B20B3905D37D2F76DB408FF9398B1707 /* Products */ = { - isa = PBXGroup; - children = ( - 507F6F7DD3B262AE27DF459A69448716 /* FBSnapshotTestCase.framework */, - 1A97A309EC8D813C2E6B909E3E845945 /* Franz.bundle */, - D10243C43EFCDCC79DA4713F236FFF2C /* Franz.framework */, - EEFA8105BF0B1AD7F976E6F7FDB3F1F2 /* Pods_Franz_Example.framework */, - CC405BC7C47C945D28D073DA88888B85 /* Pods_Franz_Tests.framework */, - ); - name = Products; - sourceTree = ""; - }; - B42C7C7E294986841F4BCC9D720DC4C0 /* Support Files */ = { - isa = PBXGroup; - children = ( - 5AA71D6414ACB25B984EE048BD9EF44F /* Franz.modulemap */, - FF1DE5A1E98A0C5720684604D9FCD86D /* Franz.xcconfig */, - C7101CDBB09AB904EA9248B0F8ABA417 /* Franz-dummy.m */, - 305DFCEC761D51CE1E2B01BD8B083191 /* Franz-prefix.pch */, - B4EF0845393103A457EB0D73471210C1 /* Franz-umbrella.h */, - B8EB56319AF316402CD1A84039B560F7 /* Info.plist */, - ); - name = "Support Files"; - path = "Example/Pods/Target Support Files/Franz"; - sourceTree = ""; - }; - CD3FB7D37E46A5379C926728A416D78E /* iOS */ = { - isa = PBXGroup; - children = ( - D31B096AB4A0164C61B63F0E59AD76C9 /* Foundation.framework */, - 0528444EFC68619AD1FEB8A7CD4A01BA /* QuartzCore.framework */, - 2393D1A899C707F79A64672C46ED238F /* UIKit.framework */, - 853C84AC883B6FC690E82525E7ECB377 /* XCTest.framework */, - ); - name = iOS; - sourceTree = ""; - }; - FBD2D672921284E66F309CA3462F84D6 /* Pods */ = { - isa = PBXGroup; - children = ( - 8004835372142699707E98D241D8C23D /* FBSnapshotTestCase */, - ); - name = Pods; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXHeadersBuildPhase section */ - 1C3293277FE6BFB35152295A5857C3F8 /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - EC21143E6EE40EA12FAE77C2E9826291 /* Franz-umbrella.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 2974192312FB3B063EB083803D980203 /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - B0CC969880D86C3CFB7EDFF61E19BEED /* Pods-Franz_Tests-umbrella.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 450CAAB58D27C57EAEA8948644860B00 /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - FFC5E408CFCB9810D3387E85E15A9A7D /* Pods-Franz_Example-umbrella.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 6A3BB9E32BDAD9EDB00EDFE657BDB001 /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - 821A26883ED14BDCD7EBADD37D671D17 /* FBSnapshotTestCase.h in Headers */, - 41D8739E2720D3535592391C25507158 /* FBSnapshotTestCasePlatform.h in Headers */, - 7BEF20E09F0E864478905311672F91AB /* FBSnapshotTestController.h in Headers */, - 1F118728B3D74089480152A6B660EA9A /* UIImage+Compare.h in Headers */, - 52B0263F90696E7FD87CB146D5C5E2C1 /* UIImage+Diff.h in Headers */, - 723FDDFD804188090511DEFA8309A06B /* UIImage+Snapshot.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXHeadersBuildPhase section */ - -/* Begin PBXNativeTarget section */ - 2BF12414DBFE95956DBB5292A7BA6841 /* Franz */ = { - isa = PBXNativeTarget; - buildConfigurationList = 373FC7128AD013B2B243BD13E744D33C /* Build configuration list for PBXNativeTarget "Franz" */; - buildPhases = ( - 7F2BAA266E55645BF092DB104627B03C /* Sources */, - 8091AF417F54DE791CC7AABE3620AAB1 /* Frameworks */, - 58381083AE17B18F7D419077207C4F43 /* Resources */, - 1C3293277FE6BFB35152295A5857C3F8 /* Headers */, - ); - buildRules = ( - ); - dependencies = ( - 668C7F0C830C0E248766D47D418586B1 /* PBXTargetDependency */, - ); - name = Franz; - productName = Franz; - productReference = D10243C43EFCDCC79DA4713F236FFF2C /* Franz.framework */; - productType = "com.apple.product-type.framework"; - }; - 709EBC98230D3C4D1C634D4E5381A3DD /* Franz-Franz */ = { - isa = PBXNativeTarget; - buildConfigurationList = 07222351FE6DC661AB2BD775C756674D /* Build configuration list for PBXNativeTarget "Franz-Franz" */; - buildPhases = ( - 9BFAEB2FA1AB9474BE624BE594B327FD /* Sources */, - B6FF4273E38465E2AEE1DBAB46D55443 /* Frameworks */, - 45304505D55C933478DDDAF2EE19975D /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = "Franz-Franz"; - productName = "Franz-Franz"; - productReference = 1A97A309EC8D813C2E6B909E3E845945 /* Franz.bundle */; - productType = "com.apple.product-type.bundle"; - }; - A5C935B614DD08342117AD0B06959CB9 /* FBSnapshotTestCase */ = { - isa = PBXNativeTarget; - buildConfigurationList = 0EDC9486793060341F389ED396820195 /* Build configuration list for PBXNativeTarget "FBSnapshotTestCase" */; - buildPhases = ( - 2CC3E7452D970C2994AA3AB9C9EBB620 /* Sources */, - 96317938B7BBB0DE5B6C6F27B4A6333F /* Frameworks */, - 6A3BB9E32BDAD9EDB00EDFE657BDB001 /* Headers */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = FBSnapshotTestCase; - productName = FBSnapshotTestCase; - productReference = 507F6F7DD3B262AE27DF459A69448716 /* FBSnapshotTestCase.framework */; - productType = "com.apple.product-type.framework"; - }; - AA6573BF27F7814852004EFE0AA49911 /* Pods-Franz_Example */ = { - isa = PBXNativeTarget; - buildConfigurationList = 10C027C4BEFB4BB7B234B18E9D840FA5 /* Build configuration list for PBXNativeTarget "Pods-Franz_Example" */; - buildPhases = ( - 69C4A06E5D5BD697A8CAFC06B37889EA /* Sources */, - A8C217B3E07F1D71FBD19223E34729D1 /* Frameworks */, - 450CAAB58D27C57EAEA8948644860B00 /* Headers */, - ); - buildRules = ( - ); - dependencies = ( - 6BB969F51CC5F114164493E7DB7BB657 /* PBXTargetDependency */, - ); - name = "Pods-Franz_Example"; - productName = "Pods-Franz_Example"; - productReference = EEFA8105BF0B1AD7F976E6F7FDB3F1F2 /* Pods_Franz_Example.framework */; - productType = "com.apple.product-type.framework"; - }; - C39B8BC7952B4115AA7D501CF229E29B /* Pods-Franz_Tests */ = { - isa = PBXNativeTarget; - buildConfigurationList = 641EEEFBA380C95155B431F3CC148191 /* Build configuration list for PBXNativeTarget "Pods-Franz_Tests" */; - buildPhases = ( - F9EDAC1E8E14754EF6ABCA24C9E89773 /* Sources */, - DA2C41D0623C56F80F7EB7E1C5DA1A22 /* Frameworks */, - 2974192312FB3B063EB083803D980203 /* Headers */, - ); - buildRules = ( - ); - dependencies = ( - 7FB583B09AA971DC2ACA580E4E51379C /* PBXTargetDependency */, - 8252C4AE05FC83FE70FD89A2FD5C880A /* PBXTargetDependency */, - ); - name = "Pods-Franz_Tests"; - productName = "Pods-Franz_Tests"; - productReference = CC405BC7C47C945D28D073DA88888B85 /* Pods_Franz_Tests.framework */; - productType = "com.apple.product-type.framework"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - D41D8CD98F00B204E9800998ECF8427E /* Project object */ = { - isa = PBXProject; - attributes = { - LastSwiftUpdateCheck = 0700; - LastUpgradeCheck = 0700; - }; - buildConfigurationList = 2D8E8EC45A3A1A1D94AE762CB5028504 /* Build configuration list for PBXProject "Pods" */; - compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; - hasScannedForEncodings = 0; - knownRegions = ( - en, - ); - mainGroup = 7DB346D0F39D3F0E887471402A8071AB; - productRefGroup = B20B3905D37D2F76DB408FF9398B1707 /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - A5C935B614DD08342117AD0B06959CB9 /* FBSnapshotTestCase */, - 2BF12414DBFE95956DBB5292A7BA6841 /* Franz */, - 709EBC98230D3C4D1C634D4E5381A3DD /* Franz-Franz */, - AA6573BF27F7814852004EFE0AA49911 /* Pods-Franz_Example */, - C39B8BC7952B4115AA7D501CF229E29B /* Pods-Franz_Tests */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 45304505D55C933478DDDAF2EE19975D /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 58381083AE17B18F7D419077207C4F43 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - BF1A026373E7F73D751AC8C51262B45A /* Franz.bundle in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 2CC3E7452D970C2994AA3AB9C9EBB620 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - CC2BD0A04199B280AD93F459968CAFC2 /* FBSnapshotTestCase-dummy.m in Sources */, - 35848D70EAC9237246BDF63457729694 /* FBSnapshotTestCase.m in Sources */, - 5D14695A69EC4A8DF7488D0FC59C105C /* FBSnapshotTestCasePlatform.m in Sources */, - AA8501741ABB8D75C6C47DE086BACD37 /* FBSnapshotTestController.m in Sources */, - 52F7A46AB0053D8BB1D793B917D37BB2 /* SwiftSupport.swift in Sources */, - 788E3D9458FE9540469981BE9A0B227C /* UIImage+Compare.m in Sources */, - 163C6E8E5E7CEE1E0BEEB9CA0939DA3A /* UIImage+Diff.m in Sources */, - EDD91D0CC316AC322E1607D3749F1C0A /* UIImage+Snapshot.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 69C4A06E5D5BD697A8CAFC06B37889EA /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 172C65BF2DCBA1125B110CC888EEF1D9 /* Pods-Franz_Example-dummy.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 7F2BAA266E55645BF092DB104627B03C /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 76F973731C5ECA430084C4A0 /* OffsetFetchAPI.swift in Sources */, - 763720C31C64F67A0028E009 /* Consumer.swift in Sources */, - 61EF6F029586143469BBBA49D82646C6 /* AdminAPI.swift in Sources */, - 446491C54EF0600A87F86D39B2A506A4 /* Broker.swift in Sources */, - 76299E1B1C55882900C4EDE1 /* Cluster.swift in Sources */, - 76333B141C57CA6A00556A70 /* Group.swift in Sources */, - 2E4C870FB0C0C9B1BC1B2ECC09B9DFAF /* Connection.swift in Sources */, - A6398C850DD9F9A8EC4D386F27C79A73 /* CRC32.swift in Sources */, - D975B6057CFFA2A9E09E9C87D0642283 /* Enumerations.swift in Sources */, - F100146FF21AAB21AC21DE751BB3B26C /* Extensions.swift in Sources */, - 0F8E3DF1028C43BEF58686F910DA51A9 /* FetchAPI.swift in Sources */, - C1A4ABF392DBF83F0A85681383C8B1B7 /* Franz-dummy.m in Sources */, - 684B72C095A6F539D321750072B6995F /* GroupCoordinatorAPI.swift in Sources */, - 59F2FB04A091A000E6F8153C8E60FE49 /* GroupMembershipAPI.swift in Sources */, - 23A955053E0AED5359F6F5F07E80DB97 /* KafkaProtocol.swift in Sources */, - ED318AD42CDAB08D7802766CB82216B8 /* KafkaRequest.swift in Sources */, - 8384DFD78FD8BF8A6D04D27FA56AB554 /* KafkaResponse.swift in Sources */, - 257ED6B3AACF80A75600C6851F4740F9 /* MessageSet.swift in Sources */, - 41D0C3EF5AC6F1E2CDA22DD7E4A43AE5 /* MetadataAPI.swift in Sources */, - 82DDF40C496D87C0A83C6DF039E0481E /* OffsetAPI.swift in Sources */, - 80C79D920A1B124F0E5C1EE749E83598 /* OffsetCommitAPI.swift in Sources */, - 03B775E406BEB00CA5AD3ACA399A21C5 /* Partition.swift in Sources */, - D3CCFCE4A651A0EF30BF8BEBB838486B /* ProduceAPI.swift in Sources */, - E8F83144CB60E17DC4FCE06AC4779AE3 /* Topic.swift in Sources */, - BBE11E9AA03E61B5011FD3399011073B /* Variables.swift in Sources */, - E7FCA6609F3B9CBD9037D88DB504A178 /* Wildcard.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 9BFAEB2FA1AB9474BE624BE594B327FD /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - F9EDAC1E8E14754EF6ABCA24C9E89773 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - A3512174664A2DE37A684024116E9CD4 /* Pods-Franz_Tests-dummy.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXTargetDependency section */ - 668C7F0C830C0E248766D47D418586B1 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - name = "Franz-Franz"; - target = 709EBC98230D3C4D1C634D4E5381A3DD /* Franz-Franz */; - targetProxy = 72F8C95D640755A36BE98853640B49D8 /* PBXContainerItemProxy */; - }; - 6BB969F51CC5F114164493E7DB7BB657 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - name = Franz; - target = 2BF12414DBFE95956DBB5292A7BA6841 /* Franz */; - targetProxy = 560B5DD9E7E5DEA38DCE05CF62E19820 /* PBXContainerItemProxy */; - }; - 7FB583B09AA971DC2ACA580E4E51379C /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - name = FBSnapshotTestCase; - target = A5C935B614DD08342117AD0B06959CB9 /* FBSnapshotTestCase */; - targetProxy = DD2894197F3509352A1F7F92F71A57BB /* PBXContainerItemProxy */; - }; - 8252C4AE05FC83FE70FD89A2FD5C880A /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - name = Franz; - target = 2BF12414DBFE95956DBB5292A7BA6841 /* Franz */; - targetProxy = 6B3E59F5137FB43DD433E604E9B995B1 /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - -/* Begin XCBuildConfiguration section */ - 017323F51165FCAA8519A7BE1EBBAD91 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 42A681D3AF997235908A3089BB9BCB20 /* FBSnapshotTestCase.xcconfig */; - buildSettings = { - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = YES; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_PREFIX_HEADER = "Target Support Files/FBSnapshotTestCase/FBSnapshotTestCase-prefix.pch"; - INFOPLIST_FILE = "Target Support Files/FBSnapshotTestCase/Info.plist"; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 8.3; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MODULEMAP_FILE = "Target Support Files/FBSnapshotTestCase/FBSnapshotTestCase.modulemap"; - MTL_ENABLE_DEBUG_INFO = NO; - PRODUCT_NAME = FBSnapshotTestCase; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - TARGETED_DEVICE_FAMILY = "1,2"; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Release; - }; - 10DE1947DAC0ED28F6C0A9F9BD75D546 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COPY_PHASE_STRIP = YES; - ENABLE_NS_ASSERTIONS = NO; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_PREPROCESSOR_DEFINITIONS = "RELEASE=1"; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.3; - STRIP_INSTALLED_PRODUCT = NO; - SYMROOT = "${SRCROOT}/../build"; - VALIDATE_PRODUCT = YES; - }; - name = Release; - }; - 552D02D5BA751AC2E8790D2811D496CA /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COPY_PHASE_STRIP = NO; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_DYNAMIC_NO_PIC = NO; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_SYMBOLS_PRIVATE_EXTERN = NO; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.3; - ONLY_ACTIVE_ARCH = YES; - STRIP_INSTALLED_PRODUCT = NO; - SYMROOT = "${SRCROOT}/../build"; - }; - name = Debug; - }; - 73CE0E31F73C85076F002FD3C2F9647A /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = D5D2BB319BBC1D08C8C175651E9DCEC0 /* Pods-Franz_Example.release.xcconfig */; - buildSettings = { - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = YES; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - ENABLE_STRICT_OBJC_MSGSEND = YES; - INFOPLIST_FILE = "Target Support Files/Pods-Franz_Example/Info.plist"; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 8.3; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MACH_O_TYPE = staticlib; - MODULEMAP_FILE = "Target Support Files/Pods-Franz_Example/Pods-Franz_Example.modulemap"; - MTL_ENABLE_DEBUG_INFO = NO; - OTHER_LDFLAGS = ""; - OTHER_LIBTOOLFLAGS = ""; - PODS_ROOT = "$(SRCROOT)"; - PRODUCT_NAME = Pods_Franz_Example; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - TARGETED_DEVICE_FAMILY = "1,2"; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Release; - }; - 79BC0897D7F763B8B75478B45DA74162 /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = FF1DE5A1E98A0C5720684604D9FCD86D /* Franz.xcconfig */; - buildSettings = { - ENABLE_STRICT_OBJC_MSGSEND = YES; - PRODUCT_NAME = Franz; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - WRAPPER_EXTENSION = bundle; - }; - name = Debug; - }; - 8D8921F18998F4F617410AD22A2645B9 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 4E94E3A8DCCC076AA2D1738F42D892C7 /* Pods-Franz_Tests.release.xcconfig */; - buildSettings = { - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = YES; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - ENABLE_STRICT_OBJC_MSGSEND = YES; - INFOPLIST_FILE = "Target Support Files/Pods-Franz_Tests/Info.plist"; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 8.3; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MACH_O_TYPE = staticlib; - MODULEMAP_FILE = "Target Support Files/Pods-Franz_Tests/Pods-Franz_Tests.modulemap"; - MTL_ENABLE_DEBUG_INFO = NO; - OTHER_LDFLAGS = ""; - OTHER_LIBTOOLFLAGS = ""; - PODS_ROOT = "$(SRCROOT)"; - PRODUCT_NAME = Pods_Franz_Tests; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - TARGETED_DEVICE_FAMILY = "1,2"; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Release; - }; - 9263636507E5A6C750D7DB8581979EFE /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 42A681D3AF997235908A3089BB9BCB20 /* FBSnapshotTestCase.xcconfig */; - buildSettings = { - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = YES; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_PREFIX_HEADER = "Target Support Files/FBSnapshotTestCase/FBSnapshotTestCase-prefix.pch"; - INFOPLIST_FILE = "Target Support Files/FBSnapshotTestCase/Info.plist"; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 8.3; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MODULEMAP_FILE = "Target Support Files/FBSnapshotTestCase/FBSnapshotTestCase.modulemap"; - MTL_ENABLE_DEBUG_INFO = YES; - PRODUCT_NAME = FBSnapshotTestCase; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - TARGETED_DEVICE_FAMILY = "1,2"; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Debug; - }; - 9FA8FFF147CB5CAF027DE6B787BEB08A /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = FF1DE5A1E98A0C5720684604D9FCD86D /* Franz.xcconfig */; - buildSettings = { - ENABLE_STRICT_OBJC_MSGSEND = YES; - PRODUCT_NAME = Franz; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - WRAPPER_EXTENSION = bundle; - }; - name = Release; - }; - B96B3BDD4A3ED13307F32D925B99F82D /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 71B05C8E3C6D92BDFE8AD10416A2C201 /* Pods-Franz_Tests.debug.xcconfig */; - buildSettings = { - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = YES; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - ENABLE_STRICT_OBJC_MSGSEND = YES; - INFOPLIST_FILE = "Target Support Files/Pods-Franz_Tests/Info.plist"; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 8.3; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MACH_O_TYPE = staticlib; - MODULEMAP_FILE = "Target Support Files/Pods-Franz_Tests/Pods-Franz_Tests.modulemap"; - MTL_ENABLE_DEBUG_INFO = YES; - OTHER_LDFLAGS = ""; - OTHER_LIBTOOLFLAGS = ""; - PODS_ROOT = "$(SRCROOT)"; - PRODUCT_NAME = Pods_Franz_Tests; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - TARGETED_DEVICE_FAMILY = "1,2"; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Debug; - }; - EEFFFE6F931CC160A3110AF1E2601FE7 /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = FF1DE5A1E98A0C5720684604D9FCD86D /* Franz.xcconfig */; - buildSettings = { - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = YES; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_PREFIX_HEADER = "Target Support Files/Franz/Franz-prefix.pch"; - INFOPLIST_FILE = "Target Support Files/Franz/Info.plist"; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 8.3; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MODULEMAP_FILE = "Target Support Files/Franz/Franz.modulemap"; - MTL_ENABLE_DEBUG_INFO = YES; - PRODUCT_NAME = Franz; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - TARGETED_DEVICE_FAMILY = "1,2"; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Debug; - }; - FB54210F98FFDFDEB776961490D50D1C /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = FF1DE5A1E98A0C5720684604D9FCD86D /* Franz.xcconfig */; - buildSettings = { - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = YES; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_PREFIX_HEADER = "Target Support Files/Franz/Franz-prefix.pch"; - INFOPLIST_FILE = "Target Support Files/Franz/Info.plist"; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 8.3; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MODULEMAP_FILE = "Target Support Files/Franz/Franz.modulemap"; - MTL_ENABLE_DEBUG_INFO = NO; - PRODUCT_NAME = Franz; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - TARGETED_DEVICE_FAMILY = "1,2"; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Release; - }; - FD8BB55734D2E4AFCD17BF3B9DCD9C34 /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 0C4D78298306D849DA1A0600947F964E /* Pods-Franz_Example.debug.xcconfig */; - buildSettings = { - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = YES; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - ENABLE_STRICT_OBJC_MSGSEND = YES; - INFOPLIST_FILE = "Target Support Files/Pods-Franz_Example/Info.plist"; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 8.3; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MACH_O_TYPE = staticlib; - MODULEMAP_FILE = "Target Support Files/Pods-Franz_Example/Pods-Franz_Example.modulemap"; - MTL_ENABLE_DEBUG_INFO = YES; - OTHER_LDFLAGS = ""; - OTHER_LIBTOOLFLAGS = ""; - PODS_ROOT = "$(SRCROOT)"; - PRODUCT_NAME = Pods_Franz_Example; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - TARGETED_DEVICE_FAMILY = "1,2"; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Debug; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 07222351FE6DC661AB2BD775C756674D /* Build configuration list for PBXNativeTarget "Franz-Franz" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 79BC0897D7F763B8B75478B45DA74162 /* Debug */, - 9FA8FFF147CB5CAF027DE6B787BEB08A /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 0EDC9486793060341F389ED396820195 /* Build configuration list for PBXNativeTarget "FBSnapshotTestCase" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 9263636507E5A6C750D7DB8581979EFE /* Debug */, - 017323F51165FCAA8519A7BE1EBBAD91 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 10C027C4BEFB4BB7B234B18E9D840FA5 /* Build configuration list for PBXNativeTarget "Pods-Franz_Example" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - FD8BB55734D2E4AFCD17BF3B9DCD9C34 /* Debug */, - 73CE0E31F73C85076F002FD3C2F9647A /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 2D8E8EC45A3A1A1D94AE762CB5028504 /* Build configuration list for PBXProject "Pods" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 552D02D5BA751AC2E8790D2811D496CA /* Debug */, - 10DE1947DAC0ED28F6C0A9F9BD75D546 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 373FC7128AD013B2B243BD13E744D33C /* Build configuration list for PBXNativeTarget "Franz" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - EEFFFE6F931CC160A3110AF1E2601FE7 /* Debug */, - FB54210F98FFDFDEB776961490D50D1C /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 641EEEFBA380C95155B431F3CC148191 /* Build configuration list for PBXNativeTarget "Pods-Franz_Tests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - B96B3BDD4A3ED13307F32D925B99F82D /* Debug */, - 8D8921F18998F4F617410AD22A2645B9 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = D41D8CD98F00B204E9800998ECF8427E /* Project object */; -} diff --git a/Example/Pods/Pods.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Example/Pods/Pods.xcodeproj/project.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 919434a..0000000 --- a/Example/Pods/Pods.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/Example/Pods/Pods.xcodeproj/xcshareddata/xcschemes/Franz.xcscheme b/Example/Pods/Pods.xcodeproj/xcshareddata/xcschemes/Franz.xcscheme deleted file mode 100644 index fe357ac..0000000 --- a/Example/Pods/Pods.xcodeproj/xcshareddata/xcschemes/Franz.xcscheme +++ /dev/null @@ -1,60 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Example/Pods/Target Support Files/FBSnapshotTestCase/FBSnapshotTestCase-dummy.m b/Example/Pods/Target Support Files/FBSnapshotTestCase/FBSnapshotTestCase-dummy.m deleted file mode 100644 index fb0c8fe..0000000 --- a/Example/Pods/Target Support Files/FBSnapshotTestCase/FBSnapshotTestCase-dummy.m +++ /dev/null @@ -1,5 +0,0 @@ -#import -@interface PodsDummy_FBSnapshotTestCase : NSObject -@end -@implementation PodsDummy_FBSnapshotTestCase -@end diff --git a/Example/Pods/Target Support Files/FBSnapshotTestCase/FBSnapshotTestCase-prefix.pch b/Example/Pods/Target Support Files/FBSnapshotTestCase/FBSnapshotTestCase-prefix.pch deleted file mode 100644 index aa992a4..0000000 --- a/Example/Pods/Target Support Files/FBSnapshotTestCase/FBSnapshotTestCase-prefix.pch +++ /dev/null @@ -1,4 +0,0 @@ -#ifdef __OBJC__ -#import -#endif - diff --git a/Example/Pods/Target Support Files/FBSnapshotTestCase/FBSnapshotTestCase.modulemap b/Example/Pods/Target Support Files/FBSnapshotTestCase/FBSnapshotTestCase.modulemap deleted file mode 100644 index 733e78b..0000000 --- a/Example/Pods/Target Support Files/FBSnapshotTestCase/FBSnapshotTestCase.modulemap +++ /dev/null @@ -1,15 +0,0 @@ -framework module FBSnapshotTestCase { - umbrella header "FBSnapshotTestCase.h" - - export * - module * { export * } - - header "FBSnapshotTestCase.h" - header "FBSnapshotTestCasePlatform.h" - header "FBSnapshotTestController.h" - - private header "UIImage+Compare.h" - private header "UIImage+Diff.h" - private header "UIImage+Snapshot.h" -} - diff --git a/Example/Pods/Target Support Files/FBSnapshotTestCase/FBSnapshotTestCase.xcconfig b/Example/Pods/Target Support Files/FBSnapshotTestCase/FBSnapshotTestCase.xcconfig deleted file mode 100644 index f30c1e2..0000000 --- a/Example/Pods/Target Support Files/FBSnapshotTestCase/FBSnapshotTestCase.xcconfig +++ /dev/null @@ -1,8 +0,0 @@ -ENABLE_BITCODE = NO -FRAMEWORK_SEARCH_PATHS = $(inherited) "$(PLATFORM_DIR)/Developer/Library/Frameworks" -GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 -HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Private/FBSnapshotTestCase" "${PODS_ROOT}/Headers/Public" -OTHER_LDFLAGS = -framework "Foundation" -framework "QuartzCore" -framework "UIKit" -framework "XCTest" -OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" -PODS_ROOT = ${SRCROOT} -SKIP_INSTALL = YES \ No newline at end of file diff --git a/Example/Pods/Target Support Files/FBSnapshotTestCase/Info.plist b/Example/Pods/Target Support Files/FBSnapshotTestCase/Info.plist deleted file mode 100644 index 9f0e44c..0000000 --- a/Example/Pods/Target Support Files/FBSnapshotTestCase/Info.plist +++ /dev/null @@ -1,26 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - ${EXECUTABLE_NAME} - CFBundleIdentifier - org.cocoapods.${PRODUCT_NAME:rfc1034identifier} - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - ${PRODUCT_NAME} - CFBundlePackageType - FMWK - CFBundleShortVersionString - 2.0.7 - CFBundleSignature - ???? - CFBundleVersion - ${CURRENT_PROJECT_VERSION} - NSPrincipalClass - - - diff --git a/Example/Pods/Target Support Files/Franz/Franz-dummy.m b/Example/Pods/Target Support Files/Franz/Franz-dummy.m deleted file mode 100644 index 0721fa2..0000000 --- a/Example/Pods/Target Support Files/Franz/Franz-dummy.m +++ /dev/null @@ -1,5 +0,0 @@ -#import -@interface PodsDummy_Franz : NSObject -@end -@implementation PodsDummy_Franz -@end diff --git a/Example/Pods/Target Support Files/Franz/Franz-prefix.pch b/Example/Pods/Target Support Files/Franz/Franz-prefix.pch deleted file mode 100644 index aa992a4..0000000 --- a/Example/Pods/Target Support Files/Franz/Franz-prefix.pch +++ /dev/null @@ -1,4 +0,0 @@ -#ifdef __OBJC__ -#import -#endif - diff --git a/Example/Pods/Target Support Files/Franz/Franz-umbrella.h b/Example/Pods/Target Support Files/Franz/Franz-umbrella.h deleted file mode 100644 index 114af31..0000000 --- a/Example/Pods/Target Support Files/Franz/Franz-umbrella.h +++ /dev/null @@ -1,6 +0,0 @@ -#import - - -FOUNDATION_EXPORT double FranzVersionNumber; -FOUNDATION_EXPORT const unsigned char FranzVersionString[]; - diff --git a/Example/Pods/Target Support Files/Franz/Franz.modulemap b/Example/Pods/Target Support Files/Franz/Franz.modulemap deleted file mode 100644 index f050718..0000000 --- a/Example/Pods/Target Support Files/Franz/Franz.modulemap +++ /dev/null @@ -1,6 +0,0 @@ -framework module Franz { - umbrella header "Franz-umbrella.h" - - export * - module * { export * } -} diff --git a/Example/Pods/Target Support Files/Franz/Franz.xcconfig b/Example/Pods/Target Support Files/Franz/Franz.xcconfig deleted file mode 100644 index d9c8588..0000000 --- a/Example/Pods/Target Support Files/Franz/Franz.xcconfig +++ /dev/null @@ -1,5 +0,0 @@ -GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 -HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Private/Franz" "${PODS_ROOT}/Headers/Public" -OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" -PODS_ROOT = ${SRCROOT} -SKIP_INSTALL = YES \ No newline at end of file diff --git a/Example/Pods/Target Support Files/Franz/Info.plist b/Example/Pods/Target Support Files/Franz/Info.plist deleted file mode 100644 index f5c0280..0000000 --- a/Example/Pods/Target Support Files/Franz/Info.plist +++ /dev/null @@ -1,26 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - ${EXECUTABLE_NAME} - CFBundleIdentifier - org.cocoapods.${PRODUCT_NAME:rfc1034identifier} - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - ${PRODUCT_NAME} - CFBundlePackageType - FMWK - CFBundleShortVersionString - 0.1.1 - CFBundleSignature - ???? - CFBundleVersion - ${CURRENT_PROJECT_VERSION} - NSPrincipalClass - - - diff --git a/Example/Pods/Target Support Files/Pods-Franz_Example/Info.plist b/Example/Pods/Target Support Files/Pods-Franz_Example/Info.plist deleted file mode 100644 index 6974542..0000000 --- a/Example/Pods/Target Support Files/Pods-Franz_Example/Info.plist +++ /dev/null @@ -1,26 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - ${EXECUTABLE_NAME} - CFBundleIdentifier - org.cocoapods.${PRODUCT_NAME:rfc1034identifier} - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - ${PRODUCT_NAME} - CFBundlePackageType - FMWK - CFBundleShortVersionString - 1.0.0 - CFBundleSignature - ???? - CFBundleVersion - ${CURRENT_PROJECT_VERSION} - NSPrincipalClass - - - diff --git a/Example/Pods/Target Support Files/Pods-Franz_Example/Pods-Franz_Example-acknowledgements.markdown b/Example/Pods/Target Support Files/Pods-Franz_Example/Pods-Franz_Example-acknowledgements.markdown deleted file mode 100644 index 4363467..0000000 --- a/Example/Pods/Target Support Files/Pods-Franz_Example/Pods-Franz_Example-acknowledgements.markdown +++ /dev/null @@ -1,26 +0,0 @@ -# Acknowledgements -This application makes use of the following third party libraries: - -## Franz - -Copyright (c) 2016 kellanburket - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - -Generated by CocoaPods - http://cocoapods.org diff --git a/Example/Pods/Target Support Files/Pods-Franz_Example/Pods-Franz_Example-acknowledgements.plist b/Example/Pods/Target Support Files/Pods-Franz_Example/Pods-Franz_Example-acknowledgements.plist deleted file mode 100644 index ce31f6d..0000000 --- a/Example/Pods/Target Support Files/Pods-Franz_Example/Pods-Franz_Example-acknowledgements.plist +++ /dev/null @@ -1,56 +0,0 @@ - - - - - PreferenceSpecifiers - - - FooterText - This application makes use of the following third party libraries: - Title - Acknowledgements - Type - PSGroupSpecifier - - - FooterText - Copyright (c) 2016 kellanburket <kellan.burket@gmail.com> - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - - Title - Franz - Type - PSGroupSpecifier - - - FooterText - Generated by CocoaPods - http://cocoapods.org - Title - - Type - PSGroupSpecifier - - - StringsTable - Acknowledgements - Title - Acknowledgements - - diff --git a/Example/Pods/Target Support Files/Pods-Franz_Example/Pods-Franz_Example-dummy.m b/Example/Pods/Target Support Files/Pods-Franz_Example/Pods-Franz_Example-dummy.m deleted file mode 100644 index d9dc4b6..0000000 --- a/Example/Pods/Target Support Files/Pods-Franz_Example/Pods-Franz_Example-dummy.m +++ /dev/null @@ -1,5 +0,0 @@ -#import -@interface PodsDummy_Pods_Franz_Example : NSObject -@end -@implementation PodsDummy_Pods_Franz_Example -@end diff --git a/Example/Pods/Target Support Files/Pods-Franz_Example/Pods-Franz_Example-frameworks.sh b/Example/Pods/Target Support Files/Pods-Franz_Example/Pods-Franz_Example-frameworks.sh deleted file mode 100755 index 88fb608..0000000 --- a/Example/Pods/Target Support Files/Pods-Franz_Example/Pods-Franz_Example-frameworks.sh +++ /dev/null @@ -1,91 +0,0 @@ -#!/bin/sh -set -e - -echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" -mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" - -SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" - -install_framework() -{ - if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then - local source="${BUILT_PRODUCTS_DIR}/$1" - elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then - local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" - elif [ -r "$1" ]; then - local source="$1" - fi - - local destination="${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" - - if [ -L "${source}" ]; then - echo "Symlinked..." - source="$(readlink "${source}")" - fi - - # use filter instead of exclude so missing patterns dont' throw errors - echo "rsync -av --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" - rsync -av --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" - - local basename - basename="$(basename -s .framework "$1")" - binary="${destination}/${basename}.framework/${basename}" - if ! [ -r "$binary" ]; then - binary="${destination}/${basename}" - fi - - # Strip invalid architectures so "fat" simulator / device frameworks work on device - if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then - strip_invalid_archs "$binary" - fi - - # Resign the code if required by the build settings to avoid unstable apps - code_sign_if_enabled "${destination}/$(basename "$1")" - - # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. - if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then - local swift_runtime_libs - swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u && exit ${PIPESTATUS[0]}) - for lib in $swift_runtime_libs; do - echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" - rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" - code_sign_if_enabled "${destination}/${lib}" - done - fi -} - -# Signs a framework with the provided identity -code_sign_if_enabled() { - if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then - # Use the current code_sign_identitiy - echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" - echo "/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} --preserve-metadata=identifier,entitlements \"$1\"" - /usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} --preserve-metadata=identifier,entitlements "$1" - fi -} - -# Strip invalid architectures -strip_invalid_archs() { - binary="$1" - # Get architectures for current file - archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | rev)" - stripped="" - for arch in $archs; do - if ! [[ "${VALID_ARCHS}" == *"$arch"* ]]; then - # Strip non-valid architectures in-place - lipo -remove "$arch" -output "$binary" "$binary" || exit 1 - stripped="$stripped $arch" - fi - done - if [[ "$stripped" ]]; then - echo "Stripped $binary of architectures:$stripped" - fi -} - - -if [[ "$CONFIGURATION" == "Debug" ]]; then - install_framework "Pods-Franz_Example/Franz.framework" -fi -if [[ "$CONFIGURATION" == "Release" ]]; then - install_framework "Pods-Franz_Example/Franz.framework" -fi diff --git a/Example/Pods/Target Support Files/Pods-Franz_Example/Pods-Franz_Example-resources.sh b/Example/Pods/Target Support Files/Pods-Franz_Example/Pods-Franz_Example-resources.sh deleted file mode 100755 index 16774fb..0000000 --- a/Example/Pods/Target Support Files/Pods-Franz_Example/Pods-Franz_Example-resources.sh +++ /dev/null @@ -1,95 +0,0 @@ -#!/bin/sh -set -e - -mkdir -p "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" - -RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt -> "$RESOURCES_TO_COPY" - -XCASSET_FILES=() - -realpath() { - DIRECTORY="$(cd "${1%/*}" && pwd)" - FILENAME="${1##*/}" - echo "$DIRECTORY/$FILENAME" -} - -install_resource() -{ - case $1 in - *.storyboard) - echo "ibtool --reference-external-strings-file --errors --warnings --notices --output-format human-readable-text --compile ${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$1\" .storyboard`.storyboardc ${PODS_ROOT}/$1 --sdk ${SDKROOT}" - ibtool --reference-external-strings-file --errors --warnings --notices --output-format human-readable-text --compile "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$1\" .storyboard`.storyboardc" "${PODS_ROOT}/$1" --sdk "${SDKROOT}" - ;; - *.xib) - echo "ibtool --reference-external-strings-file --errors --warnings --notices --output-format human-readable-text --compile ${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$1\" .xib`.nib ${PODS_ROOT}/$1 --sdk ${SDKROOT}" - ibtool --reference-external-strings-file --errors --warnings --notices --output-format human-readable-text --compile "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$1\" .xib`.nib" "${PODS_ROOT}/$1" --sdk "${SDKROOT}" - ;; - *.framework) - echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" - mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" - echo "rsync -av ${PODS_ROOT}/$1 ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" - rsync -av "${PODS_ROOT}/$1" "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" - ;; - *.xcdatamodel) - echo "xcrun momc \"${PODS_ROOT}/$1\" \"${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1"`.mom\"" - xcrun momc "${PODS_ROOT}/$1" "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcdatamodel`.mom" - ;; - *.xcdatamodeld) - echo "xcrun momc \"${PODS_ROOT}/$1\" \"${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcdatamodeld`.momd\"" - xcrun momc "${PODS_ROOT}/$1" "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcdatamodeld`.momd" - ;; - *.xcmappingmodel) - echo "xcrun mapc \"${PODS_ROOT}/$1\" \"${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcmappingmodel`.cdm\"" - xcrun mapc "${PODS_ROOT}/$1" "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcmappingmodel`.cdm" - ;; - *.xcassets) - ABSOLUTE_XCASSET_FILE=$(realpath "${PODS_ROOT}/$1") - XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE") - ;; - /*) - echo "$1" - echo "$1" >> "$RESOURCES_TO_COPY" - ;; - *) - echo "${PODS_ROOT}/$1" - echo "${PODS_ROOT}/$1" >> "$RESOURCES_TO_COPY" - ;; - esac -} - -mkdir -p "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" -rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" -if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then - mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" - rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" -fi -rm -f "$RESOURCES_TO_COPY" - -if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "$XCASSET_FILES" ] -then - case "${TARGETED_DEVICE_FAMILY}" in - 1,2) - TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone" - ;; - 1) - TARGET_DEVICE_ARGS="--target-device iphone" - ;; - 2) - TARGET_DEVICE_ARGS="--target-device ipad" - ;; - *) - TARGET_DEVICE_ARGS="--target-device mac" - ;; - esac - - # Find all other xcassets (this unfortunately includes those of path pods and other targets). - OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d) - while read line; do - if [[ $line != "`realpath $PODS_ROOT`*" ]]; then - XCASSET_FILES+=("$line") - fi - done <<<"$OTHER_XCASSETS" - - printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${IPHONEOS_DEPLOYMENT_TARGET}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" -fi diff --git a/Example/Pods/Target Support Files/Pods-Franz_Example/Pods-Franz_Example-umbrella.h b/Example/Pods/Target Support Files/Pods-Franz_Example/Pods-Franz_Example-umbrella.h deleted file mode 100644 index b5f4703..0000000 --- a/Example/Pods/Target Support Files/Pods-Franz_Example/Pods-Franz_Example-umbrella.h +++ /dev/null @@ -1,6 +0,0 @@ -#import - - -FOUNDATION_EXPORT double Pods_Franz_ExampleVersionNumber; -FOUNDATION_EXPORT const unsigned char Pods_Franz_ExampleVersionString[]; - diff --git a/Example/Pods/Target Support Files/Pods-Franz_Example/Pods-Franz_Example.debug.xcconfig b/Example/Pods/Target Support Files/Pods-Franz_Example/Pods-Franz_Example.debug.xcconfig deleted file mode 100644 index f9866aa..0000000 --- a/Example/Pods/Target Support Files/Pods-Franz_Example/Pods-Franz_Example.debug.xcconfig +++ /dev/null @@ -1,8 +0,0 @@ -EMBEDDED_CONTENT_CONTAINS_SWIFT = YES -GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 -LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' -OTHER_CFLAGS = $(inherited) -iquote "$CONFIGURATION_BUILD_DIR/Franz.framework/Headers" -OTHER_LDFLAGS = $(inherited) -framework "Franz" -OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" -PODS_FRAMEWORK_BUILD_PATH = $(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/Pods-Franz_Example -PODS_ROOT = ${SRCROOT}/Pods \ No newline at end of file diff --git a/Example/Pods/Target Support Files/Pods-Franz_Example/Pods-Franz_Example.modulemap b/Example/Pods/Target Support Files/Pods-Franz_Example/Pods-Franz_Example.modulemap deleted file mode 100644 index 32dbc4e..0000000 --- a/Example/Pods/Target Support Files/Pods-Franz_Example/Pods-Franz_Example.modulemap +++ /dev/null @@ -1,6 +0,0 @@ -framework module Pods_Franz_Example { - umbrella header "Pods-Franz_Example-umbrella.h" - - export * - module * { export * } -} diff --git a/Example/Pods/Target Support Files/Pods-Franz_Example/Pods-Franz_Example.release.xcconfig b/Example/Pods/Target Support Files/Pods-Franz_Example/Pods-Franz_Example.release.xcconfig deleted file mode 100644 index f9866aa..0000000 --- a/Example/Pods/Target Support Files/Pods-Franz_Example/Pods-Franz_Example.release.xcconfig +++ /dev/null @@ -1,8 +0,0 @@ -EMBEDDED_CONTENT_CONTAINS_SWIFT = YES -GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 -LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' -OTHER_CFLAGS = $(inherited) -iquote "$CONFIGURATION_BUILD_DIR/Franz.framework/Headers" -OTHER_LDFLAGS = $(inherited) -framework "Franz" -OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" -PODS_FRAMEWORK_BUILD_PATH = $(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/Pods-Franz_Example -PODS_ROOT = ${SRCROOT}/Pods \ No newline at end of file diff --git a/Example/Pods/Target Support Files/Pods-Franz_Tests/Info.plist b/Example/Pods/Target Support Files/Pods-Franz_Tests/Info.plist deleted file mode 100644 index 6974542..0000000 --- a/Example/Pods/Target Support Files/Pods-Franz_Tests/Info.plist +++ /dev/null @@ -1,26 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - ${EXECUTABLE_NAME} - CFBundleIdentifier - org.cocoapods.${PRODUCT_NAME:rfc1034identifier} - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - ${PRODUCT_NAME} - CFBundlePackageType - FMWK - CFBundleShortVersionString - 1.0.0 - CFBundleSignature - ???? - CFBundleVersion - ${CURRENT_PROJECT_VERSION} - NSPrincipalClass - - - diff --git a/Example/Pods/Target Support Files/Pods-Franz_Tests/Pods-Franz_Tests-acknowledgements.markdown b/Example/Pods/Target Support Files/Pods-Franz_Tests/Pods-Franz_Tests-acknowledgements.markdown deleted file mode 100644 index 65fd7ec..0000000 --- a/Example/Pods/Target Support Files/Pods-Franz_Tests/Pods-Franz_Tests-acknowledgements.markdown +++ /dev/null @@ -1,59 +0,0 @@ -# Acknowledgements -This application makes use of the following third party libraries: - -## Franz - -Copyright (c) 2016 kellanburket - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - - -## FBSnapshotTestCase - -BSD License - -For the FBSnapshotTestCase software - -Copyright (c) 2013, Facebook, Inc. -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - * Neither the name Facebook nor the names of its contributors may be used to - endorse or promote products derived from this software without specific - prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -Generated by CocoaPods - http://cocoapods.org diff --git a/Example/Pods/Target Support Files/Pods-Franz_Tests/Pods-Franz_Tests-acknowledgements.plist b/Example/Pods/Target Support Files/Pods-Franz_Tests/Pods-Franz_Tests-acknowledgements.plist deleted file mode 100644 index d6a543d..0000000 --- a/Example/Pods/Target Support Files/Pods-Franz_Tests/Pods-Franz_Tests-acknowledgements.plist +++ /dev/null @@ -1,93 +0,0 @@ - - - - - PreferenceSpecifiers - - - FooterText - This application makes use of the following third party libraries: - Title - Acknowledgements - Type - PSGroupSpecifier - - - FooterText - Copyright (c) 2016 kellanburket <kellan.burket@gmail.com> - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - - Title - Franz - Type - PSGroupSpecifier - - - FooterText - BSD License - -For the FBSnapshotTestCase software - -Copyright (c) 2013, Facebook, Inc. -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - * Neither the name Facebook nor the names of its contributors may be used to - endorse or promote products derived from this software without specific - prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - Title - FBSnapshotTestCase - Type - PSGroupSpecifier - - - FooterText - Generated by CocoaPods - http://cocoapods.org - Title - - Type - PSGroupSpecifier - - - StringsTable - Acknowledgements - Title - Acknowledgements - - diff --git a/Example/Pods/Target Support Files/Pods-Franz_Tests/Pods-Franz_Tests-dummy.m b/Example/Pods/Target Support Files/Pods-Franz_Tests/Pods-Franz_Tests-dummy.m deleted file mode 100644 index 41ffa37..0000000 --- a/Example/Pods/Target Support Files/Pods-Franz_Tests/Pods-Franz_Tests-dummy.m +++ /dev/null @@ -1,5 +0,0 @@ -#import -@interface PodsDummy_Pods_Franz_Tests : NSObject -@end -@implementation PodsDummy_Pods_Franz_Tests -@end diff --git a/Example/Pods/Target Support Files/Pods-Franz_Tests/Pods-Franz_Tests-frameworks.sh b/Example/Pods/Target Support Files/Pods-Franz_Tests/Pods-Franz_Tests-frameworks.sh deleted file mode 100755 index 35c5e5c..0000000 --- a/Example/Pods/Target Support Files/Pods-Franz_Tests/Pods-Franz_Tests-frameworks.sh +++ /dev/null @@ -1,93 +0,0 @@ -#!/bin/sh -set -e - -echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" -mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" - -SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" - -install_framework() -{ - if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then - local source="${BUILT_PRODUCTS_DIR}/$1" - elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then - local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" - elif [ -r "$1" ]; then - local source="$1" - fi - - local destination="${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" - - if [ -L "${source}" ]; then - echo "Symlinked..." - source="$(readlink "${source}")" - fi - - # use filter instead of exclude so missing patterns dont' throw errors - echo "rsync -av --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" - rsync -av --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" - - local basename - basename="$(basename -s .framework "$1")" - binary="${destination}/${basename}.framework/${basename}" - if ! [ -r "$binary" ]; then - binary="${destination}/${basename}" - fi - - # Strip invalid architectures so "fat" simulator / device frameworks work on device - if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then - strip_invalid_archs "$binary" - fi - - # Resign the code if required by the build settings to avoid unstable apps - code_sign_if_enabled "${destination}/$(basename "$1")" - - # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. - if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then - local swift_runtime_libs - swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u && exit ${PIPESTATUS[0]}) - for lib in $swift_runtime_libs; do - echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" - rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" - code_sign_if_enabled "${destination}/${lib}" - done - fi -} - -# Signs a framework with the provided identity -code_sign_if_enabled() { - if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then - # Use the current code_sign_identitiy - echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" - echo "/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} --preserve-metadata=identifier,entitlements \"$1\"" - /usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} --preserve-metadata=identifier,entitlements "$1" - fi -} - -# Strip invalid architectures -strip_invalid_archs() { - binary="$1" - # Get architectures for current file - archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | rev)" - stripped="" - for arch in $archs; do - if ! [[ "${VALID_ARCHS}" == *"$arch"* ]]; then - # Strip non-valid architectures in-place - lipo -remove "$arch" -output "$binary" "$binary" || exit 1 - stripped="$stripped $arch" - fi - done - if [[ "$stripped" ]]; then - echo "Stripped $binary of architectures:$stripped" - fi -} - - -if [[ "$CONFIGURATION" == "Debug" ]]; then - install_framework "Pods-Franz_Tests/Franz.framework" - install_framework "Pods-Franz_Tests/FBSnapshotTestCase.framework" -fi -if [[ "$CONFIGURATION" == "Release" ]]; then - install_framework "Pods-Franz_Tests/Franz.framework" - install_framework "Pods-Franz_Tests/FBSnapshotTestCase.framework" -fi diff --git a/Example/Pods/Target Support Files/Pods-Franz_Tests/Pods-Franz_Tests-resources.sh b/Example/Pods/Target Support Files/Pods-Franz_Tests/Pods-Franz_Tests-resources.sh deleted file mode 100755 index 16774fb..0000000 --- a/Example/Pods/Target Support Files/Pods-Franz_Tests/Pods-Franz_Tests-resources.sh +++ /dev/null @@ -1,95 +0,0 @@ -#!/bin/sh -set -e - -mkdir -p "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" - -RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt -> "$RESOURCES_TO_COPY" - -XCASSET_FILES=() - -realpath() { - DIRECTORY="$(cd "${1%/*}" && pwd)" - FILENAME="${1##*/}" - echo "$DIRECTORY/$FILENAME" -} - -install_resource() -{ - case $1 in - *.storyboard) - echo "ibtool --reference-external-strings-file --errors --warnings --notices --output-format human-readable-text --compile ${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$1\" .storyboard`.storyboardc ${PODS_ROOT}/$1 --sdk ${SDKROOT}" - ibtool --reference-external-strings-file --errors --warnings --notices --output-format human-readable-text --compile "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$1\" .storyboard`.storyboardc" "${PODS_ROOT}/$1" --sdk "${SDKROOT}" - ;; - *.xib) - echo "ibtool --reference-external-strings-file --errors --warnings --notices --output-format human-readable-text --compile ${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$1\" .xib`.nib ${PODS_ROOT}/$1 --sdk ${SDKROOT}" - ibtool --reference-external-strings-file --errors --warnings --notices --output-format human-readable-text --compile "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$1\" .xib`.nib" "${PODS_ROOT}/$1" --sdk "${SDKROOT}" - ;; - *.framework) - echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" - mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" - echo "rsync -av ${PODS_ROOT}/$1 ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" - rsync -av "${PODS_ROOT}/$1" "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" - ;; - *.xcdatamodel) - echo "xcrun momc \"${PODS_ROOT}/$1\" \"${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1"`.mom\"" - xcrun momc "${PODS_ROOT}/$1" "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcdatamodel`.mom" - ;; - *.xcdatamodeld) - echo "xcrun momc \"${PODS_ROOT}/$1\" \"${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcdatamodeld`.momd\"" - xcrun momc "${PODS_ROOT}/$1" "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcdatamodeld`.momd" - ;; - *.xcmappingmodel) - echo "xcrun mapc \"${PODS_ROOT}/$1\" \"${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcmappingmodel`.cdm\"" - xcrun mapc "${PODS_ROOT}/$1" "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcmappingmodel`.cdm" - ;; - *.xcassets) - ABSOLUTE_XCASSET_FILE=$(realpath "${PODS_ROOT}/$1") - XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE") - ;; - /*) - echo "$1" - echo "$1" >> "$RESOURCES_TO_COPY" - ;; - *) - echo "${PODS_ROOT}/$1" - echo "${PODS_ROOT}/$1" >> "$RESOURCES_TO_COPY" - ;; - esac -} - -mkdir -p "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" -rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" -if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then - mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" - rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" -fi -rm -f "$RESOURCES_TO_COPY" - -if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "$XCASSET_FILES" ] -then - case "${TARGETED_DEVICE_FAMILY}" in - 1,2) - TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone" - ;; - 1) - TARGET_DEVICE_ARGS="--target-device iphone" - ;; - 2) - TARGET_DEVICE_ARGS="--target-device ipad" - ;; - *) - TARGET_DEVICE_ARGS="--target-device mac" - ;; - esac - - # Find all other xcassets (this unfortunately includes those of path pods and other targets). - OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d) - while read line; do - if [[ $line != "`realpath $PODS_ROOT`*" ]]; then - XCASSET_FILES+=("$line") - fi - done <<<"$OTHER_XCASSETS" - - printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${IPHONEOS_DEPLOYMENT_TARGET}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" -fi diff --git a/Example/Pods/Target Support Files/Pods-Franz_Tests/Pods-Franz_Tests-umbrella.h b/Example/Pods/Target Support Files/Pods-Franz_Tests/Pods-Franz_Tests-umbrella.h deleted file mode 100644 index 945feee..0000000 --- a/Example/Pods/Target Support Files/Pods-Franz_Tests/Pods-Franz_Tests-umbrella.h +++ /dev/null @@ -1,6 +0,0 @@ -#import - - -FOUNDATION_EXPORT double Pods_Franz_TestsVersionNumber; -FOUNDATION_EXPORT const unsigned char Pods_Franz_TestsVersionString[]; - diff --git a/Example/Pods/Target Support Files/Pods-Franz_Tests/Pods-Franz_Tests.debug.xcconfig b/Example/Pods/Target Support Files/Pods-Franz_Tests/Pods-Franz_Tests.debug.xcconfig deleted file mode 100644 index ee2b966..0000000 --- a/Example/Pods/Target Support Files/Pods-Franz_Tests/Pods-Franz_Tests.debug.xcconfig +++ /dev/null @@ -1,8 +0,0 @@ -EMBEDDED_CONTENT_CONTAINS_SWIFT = YES -GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 -LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' -OTHER_CFLAGS = $(inherited) -iquote "$CONFIGURATION_BUILD_DIR/FBSnapshotTestCase.framework/Headers" -iquote "$CONFIGURATION_BUILD_DIR/Franz.framework/Headers" -OTHER_LDFLAGS = $(inherited) -framework "FBSnapshotTestCase" -framework "Franz" -OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" -PODS_FRAMEWORK_BUILD_PATH = $(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/Pods-Franz_Tests -PODS_ROOT = ${SRCROOT}/Pods \ No newline at end of file diff --git a/Example/Pods/Target Support Files/Pods-Franz_Tests/Pods-Franz_Tests.modulemap b/Example/Pods/Target Support Files/Pods-Franz_Tests/Pods-Franz_Tests.modulemap deleted file mode 100644 index 80e00f0..0000000 --- a/Example/Pods/Target Support Files/Pods-Franz_Tests/Pods-Franz_Tests.modulemap +++ /dev/null @@ -1,6 +0,0 @@ -framework module Pods_Franz_Tests { - umbrella header "Pods-Franz_Tests-umbrella.h" - - export * - module * { export * } -} diff --git a/Example/Pods/Target Support Files/Pods-Franz_Tests/Pods-Franz_Tests.release.xcconfig b/Example/Pods/Target Support Files/Pods-Franz_Tests/Pods-Franz_Tests.release.xcconfig deleted file mode 100644 index ee2b966..0000000 --- a/Example/Pods/Target Support Files/Pods-Franz_Tests/Pods-Franz_Tests.release.xcconfig +++ /dev/null @@ -1,8 +0,0 @@ -EMBEDDED_CONTENT_CONTAINS_SWIFT = YES -GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 -LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' -OTHER_CFLAGS = $(inherited) -iquote "$CONFIGURATION_BUILD_DIR/FBSnapshotTestCase.framework/Headers" -iquote "$CONFIGURATION_BUILD_DIR/Franz.framework/Headers" -OTHER_LDFLAGS = $(inherited) -framework "FBSnapshotTestCase" -framework "Franz" -OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" -PODS_FRAMEWORK_BUILD_PATH = $(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/Pods-Franz_Tests -PODS_ROOT = ${SRCROOT}/Pods \ No newline at end of file diff --git a/Example/Tests/Tests.swift b/Example/Tests/Tests.swift deleted file mode 100644 index c347391..0000000 --- a/Example/Tests/Tests.swift +++ /dev/null @@ -1,21 +0,0 @@ -import UIKit -import XCTest -import Franz - -class Tests: XCTestCase { - - override func setUp() { - super.setUp() - // Put setup code here. This method is called before the invocation of each test method in the class. - } - - override func tearDown() { - // Put teardown code here. This method is called after the invocation of each test method in the class. - super.tearDown() - } - - func testExample() { - // This is an example of a functional test case. - } - -} diff --git a/Franz.xcodeproj/project.pbxproj b/Franz.xcodeproj/project.pbxproj index afb2b64..6f60d9b 100644 --- a/Franz.xcodeproj/project.pbxproj +++ b/Franz.xcodeproj/project.pbxproj @@ -1,569 +1,705 @@ // !$*UTF8*$! { - archiveVersion = 1; - classes = { - }; - objectVersion = 46; - objects = { - -/* Begin PBXAggregateTarget section */ - "franz::franzPackageTests::ProductTarget" /* FranzPackageTests */ = { - isa = PBXAggregateTarget; - buildConfigurationList = OBJ_84 /* Build configuration list for PBXAggregateTarget "FranzPackageTests" */; - buildPhases = ( - ); - dependencies = ( - OBJ_87 /* PBXTargetDependency */, - ); - name = FranzPackageTests; - productName = franzPackageTests; - }; -/* End PBXAggregateTarget section */ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { /* Begin PBXBuildFile section */ - A314CB8D1F08B457008D61A6 /* CRCTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A314CB8C1F08B457008D61A6 /* CRCTests.swift */; }; - OBJ_48 /* FranzTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_37 /* FranzTests.swift */; }; - OBJ_50 /* Franz.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = "franz::franz::Product" /* Franz.framework */; }; - OBJ_57 /* AdminAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_9 /* AdminAPI.swift */; }; - OBJ_58 /* Broker.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_10 /* Broker.swift */; }; - OBJ_59 /* Cluster.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_11 /* Cluster.swift */; }; - OBJ_60 /* Connection.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_12 /* Connection.swift */; }; - OBJ_61 /* Consumer.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_13 /* Consumer.swift */; }; - OBJ_62 /* CRC32.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_15 /* CRC32.swift */; }; - OBJ_63 /* Enumerations.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_16 /* Enumerations.swift */; }; - OBJ_64 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_17 /* Extensions.swift */; }; - OBJ_65 /* FetchAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_18 /* FetchAPI.swift */; }; - OBJ_66 /* Group.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_19 /* Group.swift */; }; - OBJ_67 /* GroupCoordinatorAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_20 /* GroupCoordinatorAPI.swift */; }; - OBJ_68 /* GroupMembershipAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_21 /* GroupMembershipAPI.swift */; }; - OBJ_69 /* KafkaProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_22 /* KafkaProtocol.swift */; }; - OBJ_70 /* KafkaRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_23 /* KafkaRequest.swift */; }; - OBJ_71 /* KafkaResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_24 /* KafkaResponse.swift */; }; - OBJ_72 /* MessageSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_25 /* MessageSet.swift */; }; - OBJ_73 /* MetadataAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_26 /* MetadataAPI.swift */; }; - OBJ_74 /* OffsetAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_27 /* OffsetAPI.swift */; }; - OBJ_75 /* OffsetCommitAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_28 /* OffsetCommitAPI.swift */; }; - OBJ_76 /* OffsetFetchAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_29 /* OffsetFetchAPI.swift */; }; - OBJ_77 /* Partition.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_30 /* Partition.swift */; }; - OBJ_78 /* ProduceAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_31 /* ProduceAPI.swift */; }; - OBJ_79 /* Topic.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_32 /* Topic.swift */; }; - OBJ_80 /* Variables.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_33 /* Variables.swift */; }; - OBJ_81 /* Wildcard.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_34 /* Wildcard.swift */; }; + A314CB8D1F08B457008D61A6 /* CRCTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A314CB8C1F08B457008D61A6 /* CRCTests.swift */; }; + A32A62E11F43B733006F9111 /* ArrayProtocolTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A32A62E01F43B733006F9111 /* ArrayProtocolTests.swift */; }; + A3426BA51F25267500579CF8 /* AssignmentTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A3426BA41F25267500579CF8 /* AssignmentTests.swift */; }; + A353E8FF1F42325F00CCB63D /* IntegerProtocolTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A353E8FE1F42325F00CCB63D /* IntegerProtocolTests.swift */; }; + A3850F501F4B067800C62CF1 /* MessageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A3850F4F1F4B067800C62CF1 /* MessageTests.swift */; }; + A38623331F43A3BE005590B8 /* VariableLengthProtocolTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A38623321F43A3BE005590B8 /* VariableLengthProtocolTests.swift */; }; + A386B8091F196A730086BFF2 /* Assignment.swift in Sources */ = {isa = PBXBuildFile; fileRef = A386B8081F196A730086BFF2 /* Assignment.swift */; }; + A38E69A01F391BCB0044D3D3 /* OldConsumers.swift in Sources */ = {isa = PBXBuildFile; fileRef = A38E699F1F391BCB0044D3D3 /* OldConsumers.swift */; }; + A38E69A11F391C350044D3D3 /* Consumer.swift in Sources */ = {isa = PBXBuildFile; fileRef = A38E699D1F391B960044D3D3 /* Consumer.swift */; }; + A3CE7D951F5A004F00FBB198 /* Franz.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = "franz::franz::Product" /* Franz.framework */; }; + A3CE7D9B1F5A006100FBB198 /* ConsumerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A353FF781F2FF3B40047EE2C /* ConsumerTests.swift */; }; + A3CE7D9C1F5A006100FBB198 /* DockerTestBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = A3850F521F4B3C2F00C62CF1 /* DockerTestBase.swift */; }; + A3CE7D9E1F5A066100FBB198 /* docker-compose.yml in Resources */ = {isa = PBXBuildFile; fileRef = A32DAE311F48FA8F00D7A6A5 /* docker-compose.yml */; }; + OBJ_50 /* Franz.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = "franz::franz::Product" /* Franz.framework */; }; + OBJ_57 /* AdminAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_9 /* AdminAPI.swift */; }; + OBJ_58 /* Broker.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_10 /* Broker.swift */; }; + OBJ_59 /* Cluster.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_11 /* Cluster.swift */; }; + OBJ_60 /* Connection.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_12 /* Connection.swift */; }; + OBJ_62 /* CRC32.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_15 /* CRC32.swift */; }; + OBJ_63 /* Enumerations.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_16 /* Enumerations.swift */; }; + OBJ_65 /* FetchAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_18 /* FetchAPI.swift */; }; + OBJ_66 /* Group.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_19 /* Group.swift */; }; + OBJ_67 /* GroupCoordinatorAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_20 /* GroupCoordinatorAPI.swift */; }; + OBJ_68 /* GroupMembershipAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_21 /* GroupMembershipAPI.swift */; }; + OBJ_69 /* KafkaProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_22 /* KafkaProtocol.swift */; }; + OBJ_70 /* KafkaRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_23 /* KafkaRequest.swift */; }; + OBJ_71 /* KafkaResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_24 /* KafkaResponse.swift */; }; + OBJ_72 /* MessageSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_25 /* MessageSet.swift */; }; + OBJ_73 /* MetadataAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_26 /* MetadataAPI.swift */; }; + OBJ_74 /* OffsetAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_27 /* OffsetAPI.swift */; }; + OBJ_75 /* OffsetCommitAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_28 /* OffsetCommitAPI.swift */; }; + OBJ_76 /* OffsetFetchAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_29 /* OffsetFetchAPI.swift */; }; + OBJ_77 /* Partition.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_30 /* Partition.swift */; }; + OBJ_78 /* ProduceAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_31 /* ProduceAPI.swift */; }; + OBJ_79 /* Topic.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_32 /* Topic.swift */; }; + OBJ_80 /* Variables.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_33 /* Variables.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ - A398296C1F08245200C96CC2 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = OBJ_1 /* Project object */; - proxyType = 1; - remoteGlobalIDString = "franz::franz"; - remoteInfo = franz; - }; - A398296D1F08245300C96CC2 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = OBJ_1 /* Project object */; - proxyType = 1; - remoteGlobalIDString = "franz::franzTests"; - remoteInfo = franzTests; - }; + A398296C1F08245200C96CC2 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = OBJ_1 /* Project object */; + proxyType = 1; + remoteGlobalIDString = "franz::franz"; + remoteInfo = franz; + }; + A3CE7D961F5A004F00FBB198 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = OBJ_1 /* Project object */; + proxyType = 1; + remoteGlobalIDString = "franz::franz"; + remoteInfo = Franz; + }; /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ - A314CB8C1F08B457008D61A6 /* CRCTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CRCTests.swift; sourceTree = ""; }; - OBJ_10 /* Broker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Broker.swift; sourceTree = ""; }; - OBJ_11 /* Cluster.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Cluster.swift; sourceTree = ""; }; - OBJ_12 /* Connection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Connection.swift; sourceTree = ""; }; - OBJ_13 /* Consumer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Consumer.swift; sourceTree = ""; }; - OBJ_15 /* CRC32.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CRC32.swift; sourceTree = ""; }; - OBJ_16 /* Enumerations.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Enumerations.swift; sourceTree = ""; }; - OBJ_17 /* Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Extensions.swift; sourceTree = ""; }; - OBJ_18 /* FetchAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FetchAPI.swift; sourceTree = ""; }; - OBJ_19 /* Group.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Group.swift; sourceTree = ""; }; - OBJ_20 /* GroupCoordinatorAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupCoordinatorAPI.swift; sourceTree = ""; }; - OBJ_21 /* GroupMembershipAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupMembershipAPI.swift; sourceTree = ""; }; - OBJ_22 /* KafkaProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KafkaProtocol.swift; sourceTree = ""; }; - OBJ_23 /* KafkaRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KafkaRequest.swift; sourceTree = ""; }; - OBJ_24 /* KafkaResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KafkaResponse.swift; sourceTree = ""; }; - OBJ_25 /* MessageSet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageSet.swift; sourceTree = ""; }; - OBJ_26 /* MetadataAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MetadataAPI.swift; sourceTree = ""; }; - OBJ_27 /* OffsetAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OffsetAPI.swift; sourceTree = ""; }; - OBJ_28 /* OffsetCommitAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OffsetCommitAPI.swift; sourceTree = ""; }; - OBJ_29 /* OffsetFetchAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OffsetFetchAPI.swift; sourceTree = ""; }; - OBJ_30 /* Partition.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Partition.swift; sourceTree = ""; }; - OBJ_31 /* ProduceAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProduceAPI.swift; sourceTree = ""; }; - OBJ_32 /* Topic.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Topic.swift; sourceTree = ""; }; - OBJ_33 /* Variables.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Variables.swift; sourceTree = ""; }; - OBJ_34 /* Wildcard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Wildcard.swift; sourceTree = ""; }; - OBJ_37 /* FranzTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FranzTests.swift; sourceTree = ""; }; - OBJ_6 /* Package.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; path = Package.swift; sourceTree = ""; }; - OBJ_9 /* AdminAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdminAPI.swift; sourceTree = ""; }; - "franz::franz::Product" /* Franz.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Franz.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - "franz::franzTests::Product" /* FranzTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; path = FranzTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + A314CB8C1F08B457008D61A6 /* CRCTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CRCTests.swift; sourceTree = ""; }; + A32A62E01F43B733006F9111 /* ArrayProtocolTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArrayProtocolTests.swift; sourceTree = ""; }; + A32DAE311F48FA8F00D7A6A5 /* docker-compose.yml */ = {isa = PBXFileReference; indentWidth = 2; lastKnownFileType = text; path = "docker-compose.yml"; sourceTree = ""; tabWidth = 2; usesTabs = 0; }; + A3426BA41F25267500579CF8 /* AssignmentTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AssignmentTests.swift; sourceTree = ""; }; + A353E8FE1F42325F00CCB63D /* IntegerProtocolTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IntegerProtocolTests.swift; sourceTree = ""; }; + A353FF781F2FF3B40047EE2C /* ConsumerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConsumerTests.swift; sourceTree = ""; }; + A3850F4F1F4B067800C62CF1 /* MessageTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageTests.swift; sourceTree = ""; }; + A3850F521F4B3C2F00C62CF1 /* DockerTestBase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = DockerTestBase.swift; path = DockerTests/DockerTestBase.swift; sourceTree = SOURCE_ROOT; }; + A38623321F43A3BE005590B8 /* VariableLengthProtocolTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VariableLengthProtocolTests.swift; sourceTree = ""; }; + A386B8081F196A730086BFF2 /* Assignment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Assignment.swift; sourceTree = ""; }; + A38E699D1F391B960044D3D3 /* Consumer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Consumer.swift; sourceTree = ""; }; + A38E699F1F391BCB0044D3D3 /* OldConsumers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OldConsumers.swift; sourceTree = ""; }; + A3CE7D901F5A004E00FBB198 /* DockerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = DockerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + A3CE7D941F5A004F00FBB198 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + OBJ_10 /* Broker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Broker.swift; sourceTree = ""; }; + OBJ_11 /* Cluster.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Cluster.swift; sourceTree = ""; }; + OBJ_12 /* Connection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Connection.swift; sourceTree = ""; }; + OBJ_15 /* CRC32.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CRC32.swift; sourceTree = ""; }; + OBJ_16 /* Enumerations.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Enumerations.swift; sourceTree = ""; }; + OBJ_18 /* FetchAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FetchAPI.swift; sourceTree = ""; }; + OBJ_19 /* Group.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Group.swift; sourceTree = ""; }; + OBJ_20 /* GroupCoordinatorAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupCoordinatorAPI.swift; sourceTree = ""; }; + OBJ_21 /* GroupMembershipAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupMembershipAPI.swift; sourceTree = ""; }; + OBJ_22 /* KafkaProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KafkaProtocol.swift; sourceTree = ""; }; + OBJ_23 /* KafkaRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KafkaRequest.swift; sourceTree = ""; }; + OBJ_24 /* KafkaResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KafkaResponse.swift; sourceTree = ""; }; + OBJ_25 /* MessageSet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageSet.swift; sourceTree = ""; }; + OBJ_26 /* MetadataAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MetadataAPI.swift; sourceTree = ""; }; + OBJ_27 /* OffsetAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OffsetAPI.swift; sourceTree = ""; }; + OBJ_28 /* OffsetCommitAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OffsetCommitAPI.swift; sourceTree = ""; }; + OBJ_29 /* OffsetFetchAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OffsetFetchAPI.swift; sourceTree = ""; }; + OBJ_30 /* Partition.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Partition.swift; sourceTree = ""; }; + OBJ_31 /* ProduceAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProduceAPI.swift; sourceTree = ""; }; + OBJ_32 /* Topic.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Topic.swift; sourceTree = ""; }; + OBJ_33 /* Variables.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Variables.swift; sourceTree = ""; }; + OBJ_6 /* Package.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; path = Package.swift; sourceTree = ""; }; + OBJ_9 /* AdminAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdminAPI.swift; sourceTree = ""; }; + "franz::franz::Product" /* Franz.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Franz.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + "franz::franzTests::Product" /* FranzTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; path = FranzTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ - OBJ_49 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 0; - files = ( - OBJ_50 /* Franz.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - OBJ_82 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 0; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; + A3CE7D8D1F5A004E00FBB198 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + A3CE7D951F5A004F00FBB198 /* Franz.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + OBJ_49 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 0; + files = ( + OBJ_50 /* Franz.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + OBJ_82 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 0; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - OBJ_14 /* Crypt */ = { - isa = PBXGroup; - children = ( - OBJ_15 /* CRC32.swift */, - ); - path = Crypt; - sourceTree = ""; - }; - OBJ_35 /* Tests */ = { - isa = PBXGroup; - children = ( - OBJ_36 /* FranzTests */, - ); - name = Tests; - sourceTree = SOURCE_ROOT; - }; - OBJ_36 /* FranzTests */ = { - isa = PBXGroup; - children = ( - OBJ_37 /* FranzTests.swift */, - A314CB8C1F08B457008D61A6 /* CRCTests.swift */, - ); - name = FranzTests; - path = Tests/FranzTests; - sourceTree = SOURCE_ROOT; - }; - OBJ_40 /* Products */ = { - isa = PBXGroup; - children = ( - "franz::franzTests::Product" /* FranzTests.xctest */, - "franz::franz::Product" /* Franz.framework */, - ); - name = Products; - sourceTree = BUILT_PRODUCTS_DIR; - }; - OBJ_5 = { - isa = PBXGroup; - children = ( - OBJ_6 /* Package.swift */, - OBJ_7 /* Sources */, - OBJ_35 /* Tests */, - OBJ_40 /* Products */, - ); - sourceTree = ""; - }; - OBJ_7 /* Sources */ = { - isa = PBXGroup; - children = ( - OBJ_8 /* Franz */, - ); - name = Sources; - sourceTree = SOURCE_ROOT; - }; - OBJ_8 /* Franz */ = { - isa = PBXGroup; - children = ( - OBJ_9 /* AdminAPI.swift */, - OBJ_10 /* Broker.swift */, - OBJ_11 /* Cluster.swift */, - OBJ_12 /* Connection.swift */, - OBJ_13 /* Consumer.swift */, - OBJ_14 /* Crypt */, - OBJ_16 /* Enumerations.swift */, - OBJ_17 /* Extensions.swift */, - OBJ_18 /* FetchAPI.swift */, - OBJ_19 /* Group.swift */, - OBJ_20 /* GroupCoordinatorAPI.swift */, - OBJ_21 /* GroupMembershipAPI.swift */, - OBJ_22 /* KafkaProtocol.swift */, - OBJ_23 /* KafkaRequest.swift */, - OBJ_24 /* KafkaResponse.swift */, - OBJ_25 /* MessageSet.swift */, - OBJ_26 /* MetadataAPI.swift */, - OBJ_27 /* OffsetAPI.swift */, - OBJ_28 /* OffsetCommitAPI.swift */, - OBJ_29 /* OffsetFetchAPI.swift */, - OBJ_30 /* Partition.swift */, - OBJ_31 /* ProduceAPI.swift */, - OBJ_32 /* Topic.swift */, - OBJ_33 /* Variables.swift */, - OBJ_34 /* Wildcard.swift */, - ); - name = Franz; - path = Sources/Franz; - sourceTree = SOURCE_ROOT; - }; + A38623311F43A31F005590B8 /* Protocol */ = { + isa = PBXGroup; + children = ( + A353E8FE1F42325F00CCB63D /* IntegerProtocolTests.swift */, + A38623321F43A3BE005590B8 /* VariableLengthProtocolTests.swift */, + A32A62E01F43B733006F9111 /* ArrayProtocolTests.swift */, + A3850F4F1F4B067800C62CF1 /* MessageTests.swift */, + ); + name = Protocol; + path = Tests/FranzTests/Protocol; + sourceTree = SOURCE_ROOT; + }; + A3CE7D911F5A004F00FBB198 /* DockerTests */ = { + isa = PBXGroup; + children = ( + A3850F521F4B3C2F00C62CF1 /* DockerTestBase.swift */, + A353FF781F2FF3B40047EE2C /* ConsumerTests.swift */, + A3CE7D941F5A004F00FBB198 /* Info.plist */, + A32DAE311F48FA8F00D7A6A5 /* docker-compose.yml */, + ); + path = DockerTests; + sourceTree = ""; + }; + OBJ_14 /* Crypt */ = { + isa = PBXGroup; + children = ( + OBJ_15 /* CRC32.swift */, + ); + path = Crypt; + sourceTree = ""; + }; + OBJ_35 /* Tests */ = { + isa = PBXGroup; + children = ( + A3CE7D911F5A004F00FBB198 /* DockerTests */, + OBJ_36 /* FranzTests */, + ); + name = Tests; + sourceTree = SOURCE_ROOT; + }; + OBJ_36 /* FranzTests */ = { + isa = PBXGroup; + children = ( + A3426BA41F25267500579CF8 /* AssignmentTests.swift */, + A314CB8C1F08B457008D61A6 /* CRCTests.swift */, + A38623311F43A31F005590B8 /* Protocol */, + ); + name = FranzTests; + path = Tests/FranzTests; + sourceTree = SOURCE_ROOT; + }; + OBJ_40 /* Products */ = { + isa = PBXGroup; + children = ( + "franz::franzTests::Product" /* FranzTests.xctest */, + "franz::franz::Product" /* Franz.framework */, + A3CE7D901F5A004E00FBB198 /* DockerTests.xctest */, + ); + name = Products; + sourceTree = BUILT_PRODUCTS_DIR; + }; + OBJ_5 = { + isa = PBXGroup; + children = ( + OBJ_6 /* Package.swift */, + OBJ_7 /* Sources */, + OBJ_35 /* Tests */, + OBJ_40 /* Products */, + ); + sourceTree = ""; + }; + OBJ_7 /* Sources */ = { + isa = PBXGroup; + children = ( + OBJ_8 /* Franz */, + ); + name = Sources; + sourceTree = SOURCE_ROOT; + }; + OBJ_8 /* Franz */ = { + isa = PBXGroup; + children = ( + OBJ_9 /* AdminAPI.swift */, + OBJ_10 /* Broker.swift */, + OBJ_11 /* Cluster.swift */, + OBJ_12 /* Connection.swift */, + A38E699D1F391B960044D3D3 /* Consumer.swift */, + OBJ_14 /* Crypt */, + OBJ_16 /* Enumerations.swift */, + OBJ_18 /* FetchAPI.swift */, + OBJ_19 /* Group.swift */, + OBJ_20 /* GroupCoordinatorAPI.swift */, + OBJ_21 /* GroupMembershipAPI.swift */, + OBJ_22 /* KafkaProtocol.swift */, + OBJ_23 /* KafkaRequest.swift */, + OBJ_24 /* KafkaResponse.swift */, + OBJ_25 /* MessageSet.swift */, + OBJ_26 /* MetadataAPI.swift */, + OBJ_27 /* OffsetAPI.swift */, + OBJ_28 /* OffsetCommitAPI.swift */, + OBJ_29 /* OffsetFetchAPI.swift */, + OBJ_30 /* Partition.swift */, + OBJ_31 /* ProduceAPI.swift */, + OBJ_32 /* Topic.swift */, + OBJ_33 /* Variables.swift */, + A386B8081F196A730086BFF2 /* Assignment.swift */, + A38E699F1F391BCB0044D3D3 /* OldConsumers.swift */, + ); + name = Franz; + path = Sources/Franz; + sourceTree = SOURCE_ROOT; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ - "franz::franz" /* Franz */ = { - isa = PBXNativeTarget; - buildConfigurationList = OBJ_53 /* Build configuration list for PBXNativeTarget "Franz" */; - buildPhases = ( - OBJ_56 /* Sources */, - OBJ_82 /* Frameworks */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = Franz; - productName = franz; - productReference = "franz::franz::Product" /* Franz.framework */; - productType = "com.apple.product-type.framework"; - }; - "franz::franzTests" /* FranzTests */ = { - isa = PBXNativeTarget; - buildConfigurationList = OBJ_44 /* Build configuration list for PBXNativeTarget "FranzTests" */; - buildPhases = ( - OBJ_47 /* Sources */, - OBJ_49 /* Frameworks */, - ); - buildRules = ( - ); - dependencies = ( - OBJ_51 /* PBXTargetDependency */, - ); - name = FranzTests; - productName = franzTests; - productReference = "franz::franzTests::Product" /* FranzTests.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; + A3CE7D8F1F5A004E00FBB198 /* DockerTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = A3CE7D9A1F5A004F00FBB198 /* Build configuration list for PBXNativeTarget "DockerTests" */; + buildPhases = ( + A3CE7D8C1F5A004E00FBB198 /* Sources */, + A3CE7D8D1F5A004E00FBB198 /* Frameworks */, + A3CE7D8E1F5A004E00FBB198 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + A3CE7D971F5A004F00FBB198 /* PBXTargetDependency */, + ); + name = DockerTests; + productName = DockerTests; + productReference = A3CE7D901F5A004E00FBB198 /* DockerTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + "franz::franz" /* Franz */ = { + isa = PBXNativeTarget; + buildConfigurationList = OBJ_53 /* Build configuration list for PBXNativeTarget "Franz" */; + buildPhases = ( + OBJ_56 /* Sources */, + OBJ_82 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Franz; + productName = franz; + productReference = "franz::franz::Product" /* Franz.framework */; + productType = "com.apple.product-type.framework"; + }; + "franz::franzTests" /* FranzTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = OBJ_44 /* Build configuration list for PBXNativeTarget "FranzTests" */; + buildPhases = ( + OBJ_47 /* Sources */, + OBJ_49 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + OBJ_51 /* PBXTargetDependency */, + ); + name = FranzTests; + productName = franzTests; + productReference = "franz::franzTests::Product" /* FranzTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ - OBJ_1 /* Project object */ = { - isa = PBXProject; - attributes = { - LastUpgradeCheck = 0900; - TargetAttributes = { - "franz::franz" = { - LastSwiftMigration = 0900; - }; - "franz::franzTests" = { - LastSwiftMigration = 0900; - }; - }; - }; - buildConfigurationList = OBJ_2 /* Build configuration list for PBXProject "Franz" */; - compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; - hasScannedForEncodings = 0; - knownRegions = ( - en, - ); - mainGroup = OBJ_5; - productRefGroup = OBJ_40 /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - "franz::franzTests" /* FranzTests */, - "franz::franz" /* Franz */, - "franz::franzPackageTests::ProductTarget" /* FranzPackageTests */, - ); - }; + OBJ_1 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0900; + LastUpgradeCheck = 0900; + TargetAttributes = { + A3CE7D8F1F5A004E00FBB198 = { + CreatedOnToolsVersion = 9.0; + DevelopmentTeam = 7R888D749H; + }; + "franz::franz" = { + LastSwiftMigration = 0900; + }; + "franz::franzTests" = { + LastSwiftMigration = 0900; + }; + }; + }; + buildConfigurationList = OBJ_2 /* Build configuration list for PBXProject "Franz" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = OBJ_5; + productRefGroup = OBJ_40 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + "franz::franzTests" /* FranzTests */, + "franz::franz" /* Franz */, + A3CE7D8F1F5A004E00FBB198 /* DockerTests */, + ); + }; /* End PBXProject section */ +/* Begin PBXResourcesBuildPhase section */ + A3CE7D8E1F5A004E00FBB198 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + A3CE7D9E1F5A066100FBB198 /* docker-compose.yml in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + /* Begin PBXSourcesBuildPhase section */ - OBJ_47 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 0; - files = ( - A314CB8D1F08B457008D61A6 /* CRCTests.swift in Sources */, - OBJ_48 /* FranzTests.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - OBJ_56 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 0; - files = ( - OBJ_57 /* AdminAPI.swift in Sources */, - OBJ_58 /* Broker.swift in Sources */, - OBJ_59 /* Cluster.swift in Sources */, - OBJ_60 /* Connection.swift in Sources */, - OBJ_61 /* Consumer.swift in Sources */, - OBJ_62 /* CRC32.swift in Sources */, - OBJ_63 /* Enumerations.swift in Sources */, - OBJ_64 /* Extensions.swift in Sources */, - OBJ_65 /* FetchAPI.swift in Sources */, - OBJ_66 /* Group.swift in Sources */, - OBJ_67 /* GroupCoordinatorAPI.swift in Sources */, - OBJ_68 /* GroupMembershipAPI.swift in Sources */, - OBJ_69 /* KafkaProtocol.swift in Sources */, - OBJ_70 /* KafkaRequest.swift in Sources */, - OBJ_71 /* KafkaResponse.swift in Sources */, - OBJ_72 /* MessageSet.swift in Sources */, - OBJ_73 /* MetadataAPI.swift in Sources */, - OBJ_74 /* OffsetAPI.swift in Sources */, - OBJ_75 /* OffsetCommitAPI.swift in Sources */, - OBJ_76 /* OffsetFetchAPI.swift in Sources */, - OBJ_77 /* Partition.swift in Sources */, - OBJ_78 /* ProduceAPI.swift in Sources */, - OBJ_79 /* Topic.swift in Sources */, - OBJ_80 /* Variables.swift in Sources */, - OBJ_81 /* Wildcard.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; + A3CE7D8C1F5A004E00FBB198 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + A3CE7D9C1F5A006100FBB198 /* DockerTestBase.swift in Sources */, + A3CE7D9B1F5A006100FBB198 /* ConsumerTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + OBJ_47 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 0; + files = ( + A353E8FF1F42325F00CCB63D /* IntegerProtocolTests.swift in Sources */, + A3426BA51F25267500579CF8 /* AssignmentTests.swift in Sources */, + A314CB8D1F08B457008D61A6 /* CRCTests.swift in Sources */, + A3850F501F4B067800C62CF1 /* MessageTests.swift in Sources */, + A38623331F43A3BE005590B8 /* VariableLengthProtocolTests.swift in Sources */, + A32A62E11F43B733006F9111 /* ArrayProtocolTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + OBJ_56 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 0; + files = ( + A38E69A11F391C350044D3D3 /* Consumer.swift in Sources */, + OBJ_57 /* AdminAPI.swift in Sources */, + OBJ_58 /* Broker.swift in Sources */, + OBJ_59 /* Cluster.swift in Sources */, + OBJ_60 /* Connection.swift in Sources */, + OBJ_62 /* CRC32.swift in Sources */, + OBJ_63 /* Enumerations.swift in Sources */, + OBJ_65 /* FetchAPI.swift in Sources */, + OBJ_66 /* Group.swift in Sources */, + A386B8091F196A730086BFF2 /* Assignment.swift in Sources */, + OBJ_67 /* GroupCoordinatorAPI.swift in Sources */, + A38E69A01F391BCB0044D3D3 /* OldConsumers.swift in Sources */, + OBJ_68 /* GroupMembershipAPI.swift in Sources */, + OBJ_69 /* KafkaProtocol.swift in Sources */, + OBJ_70 /* KafkaRequest.swift in Sources */, + OBJ_71 /* KafkaResponse.swift in Sources */, + OBJ_72 /* MessageSet.swift in Sources */, + OBJ_73 /* MetadataAPI.swift in Sources */, + OBJ_74 /* OffsetAPI.swift in Sources */, + OBJ_75 /* OffsetCommitAPI.swift in Sources */, + OBJ_76 /* OffsetFetchAPI.swift in Sources */, + OBJ_77 /* Partition.swift in Sources */, + OBJ_78 /* ProduceAPI.swift in Sources */, + OBJ_79 /* Topic.swift in Sources */, + OBJ_80 /* Variables.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ - OBJ_51 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = "franz::franz" /* Franz */; - targetProxy = A398296C1F08245200C96CC2 /* PBXContainerItemProxy */; - }; - OBJ_87 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = "franz::franzTests" /* FranzTests */; - targetProxy = A398296D1F08245300C96CC2 /* PBXContainerItemProxy */; - }; + A3CE7D971F5A004F00FBB198 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = "franz::franz" /* Franz */; + targetProxy = A3CE7D961F5A004F00FBB198 /* PBXContainerItemProxy */; + }; + OBJ_51 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = "franz::franz" /* Franz */; + targetProxy = A398296C1F08245200C96CC2 /* PBXContainerItemProxy */; + }; /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ - OBJ_3 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COMBINE_HIDPI_IMAGES = YES; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - ENABLE_NS_ASSERTIONS = YES; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.10; - ONLY_ACTIVE_ARCH = YES; - OTHER_SWIFT_FLAGS = "-DXcode"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = macosx; - SUPPORTED_PLATFORMS = "macosx iphoneos iphonesimulator appletvos appletvsimulator watchos watchsimulator"; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = SWIFT_PACKAGE; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - USE_HEADERMAP = NO; - }; - name = Debug; - }; - OBJ_4 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COMBINE_HIDPI_IMAGES = YES; - COPY_PHASE_STRIP = YES; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = s; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.10; - OTHER_SWIFT_FLAGS = "-DXcode"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = macosx; - SUPPORTED_PLATFORMS = "macosx iphoneos iphonesimulator appletvos appletvsimulator watchos watchsimulator"; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = SWIFT_PACKAGE; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; - USE_HEADERMAP = NO; - }; - name = Release; - }; - OBJ_45 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PLATFORM_DIR)/Developer/Library/Frameworks", - ); - HEADER_SEARCH_PATHS = "$(inherited)"; - INFOPLIST_FILE = franz.xcodeproj/franzTests_Info.plist; - LD_RUNPATH_SEARCH_PATHS = "@loader_path/../Frameworks @loader_path/Frameworks"; - OTHER_LDFLAGS = "$(inherited)"; - OTHER_SWIFT_FLAGS = "$(inherited)"; - PRODUCT_NAME = FranzTests; - SWIFT_SWIFT3_OBJC_INFERENCE = Off; - SWIFT_VERSION = 4.0; - TARGET_NAME = franzTests; - }; - name = Debug; - }; - OBJ_46 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PLATFORM_DIR)/Developer/Library/Frameworks", - ); - HEADER_SEARCH_PATHS = "$(inherited)"; - INFOPLIST_FILE = franz.xcodeproj/franzTests_Info.plist; - LD_RUNPATH_SEARCH_PATHS = "@loader_path/../Frameworks @loader_path/Frameworks"; - OTHER_LDFLAGS = "$(inherited)"; - OTHER_SWIFT_FLAGS = "$(inherited)"; - PRODUCT_NAME = FranzTests; - SWIFT_SWIFT3_OBJC_INFERENCE = Off; - SWIFT_VERSION = 4.0; - TARGET_NAME = franzTests; - }; - name = Release; - }; - OBJ_54 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ENABLE_TESTABILITY = YES; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PLATFORM_DIR)/Developer/Library/Frameworks", - ); - HEADER_SEARCH_PATHS = "$(inherited)"; - INFOPLIST_FILE = franz.xcodeproj/franz_Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(TOOLCHAIN_DIR)/usr/lib/swift/macosx"; - OTHER_LDFLAGS = "$(inherited)"; - OTHER_SWIFT_FLAGS = "$(inherited)"; - PRODUCT_BUNDLE_IDENTIFIER = franz; - PRODUCT_MODULE_NAME = Franz; - PRODUCT_NAME = Franz; - SKIP_INSTALL = YES; - SWIFT_SWIFT3_OBJC_INFERENCE = Off; - SWIFT_VERSION = 4.0; - TARGET_NAME = franz; - }; - name = Debug; - }; - OBJ_55 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ENABLE_TESTABILITY = YES; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PLATFORM_DIR)/Developer/Library/Frameworks", - ); - HEADER_SEARCH_PATHS = "$(inherited)"; - INFOPLIST_FILE = franz.xcodeproj/franz_Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(TOOLCHAIN_DIR)/usr/lib/swift/macosx"; - OTHER_LDFLAGS = "$(inherited)"; - OTHER_SWIFT_FLAGS = "$(inherited)"; - PRODUCT_BUNDLE_IDENTIFIER = franz; - PRODUCT_MODULE_NAME = Franz; - PRODUCT_NAME = Franz; - SKIP_INSTALL = YES; - SWIFT_SWIFT3_OBJC_INFERENCE = Off; - SWIFT_VERSION = 4.0; - TARGET_NAME = franz; - }; - name = Release; - }; - OBJ_85 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - PRODUCT_NAME = FranzPackageTests; - }; - name = Debug; - }; - OBJ_86 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - PRODUCT_NAME = FranzPackageTests; - }; - name = Release; - }; + A3CE7D981F5A004F00FBB198 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = "Mac Developer"; + COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 7R888D749H; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + INFOPLIST_FILE = DockerTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.13; + MTL_ENABLE_DEBUG_INFO = YES; + PRODUCT_BUNDLE_IDENTIFIER = Franz.DockerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_VERSION = 4.0; + }; + name = Debug; + }; + A3CE7D991F5A004F00FBB198 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = "Mac Developer"; + COMBINE_HIDPI_IMAGES = YES; + COPY_PHASE_STRIP = NO; + DEVELOPMENT_TEAM = 7R888D749H; + ENABLE_NS_ASSERTIONS = NO; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + INFOPLIST_FILE = DockerTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.13; + MTL_ENABLE_DEBUG_INFO = NO; + PRODUCT_BUNDLE_IDENTIFIER = Franz.DockerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 4.0; + }; + name = Release; + }; + OBJ_3 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COMBINE_HIDPI_IMAGES = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_NS_ASSERTIONS = YES; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.10; + ONLY_ACTIVE_ARCH = YES; + OTHER_SWIFT_FLAGS = "-DXcode"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + SUPPORTED_PLATFORMS = "macosx iphoneos iphonesimulator appletvos appletvsimulator watchos watchsimulator"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = SWIFT_PACKAGE; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + USE_HEADERMAP = NO; + }; + name = Debug; + }; + OBJ_4 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COMBINE_HIDPI_IMAGES = YES; + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = s; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.10; + OTHER_SWIFT_FLAGS = "-DXcode"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + SUPPORTED_PLATFORMS = "macosx iphoneos iphonesimulator appletvos appletvsimulator watchos watchsimulator"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = SWIFT_PACKAGE; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + USE_HEADERMAP = NO; + }; + name = Release; + }; + OBJ_45 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PLATFORM_DIR)/Developer/Library/Frameworks", + ); + HEADER_SEARCH_PATHS = "$(inherited)"; + INFOPLIST_FILE = franz.xcodeproj/franzTests_Info.plist; + LD_RUNPATH_SEARCH_PATHS = "@loader_path/../Frameworks @loader_path/Frameworks"; + OTHER_LDFLAGS = "$(inherited)"; + OTHER_SWIFT_FLAGS = "$(inherited)"; + PRODUCT_NAME = FranzTests; + SWIFT_SWIFT3_OBJC_INFERENCE = Off; + SWIFT_VERSION = 4.0; + TARGET_NAME = franzTests; + }; + name = Debug; + }; + OBJ_46 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PLATFORM_DIR)/Developer/Library/Frameworks", + ); + HEADER_SEARCH_PATHS = "$(inherited)"; + INFOPLIST_FILE = franz.xcodeproj/franzTests_Info.plist; + LD_RUNPATH_SEARCH_PATHS = "@loader_path/../Frameworks @loader_path/Frameworks"; + OTHER_LDFLAGS = "$(inherited)"; + OTHER_SWIFT_FLAGS = "$(inherited)"; + PRODUCT_NAME = FranzTests; + SWIFT_SWIFT3_OBJC_INFERENCE = Off; + SWIFT_VERSION = 4.0; + TARGET_NAME = franzTests; + }; + name = Release; + }; + OBJ_54 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ENABLE_TESTABILITY = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PLATFORM_DIR)/Developer/Library/Frameworks", + ); + HEADER_SEARCH_PATHS = "$(inherited)"; + INFOPLIST_FILE = franz.xcodeproj/franz_Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(TOOLCHAIN_DIR)/usr/lib/swift/macosx"; + OTHER_LDFLAGS = "$(inherited)"; + OTHER_SWIFT_FLAGS = "$(inherited)"; + PRODUCT_BUNDLE_IDENTIFIER = franz; + PRODUCT_MODULE_NAME = Franz; + PRODUCT_NAME = Franz; + SKIP_INSTALL = YES; + SWIFT_SWIFT3_OBJC_INFERENCE = Off; + SWIFT_VERSION = 4.0; + TARGET_NAME = franz; + }; + name = Debug; + }; + OBJ_55 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ENABLE_TESTABILITY = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PLATFORM_DIR)/Developer/Library/Frameworks", + ); + HEADER_SEARCH_PATHS = "$(inherited)"; + INFOPLIST_FILE = franz.xcodeproj/franz_Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(TOOLCHAIN_DIR)/usr/lib/swift/macosx"; + OTHER_LDFLAGS = "$(inherited)"; + OTHER_SWIFT_FLAGS = "$(inherited)"; + PRODUCT_BUNDLE_IDENTIFIER = franz; + PRODUCT_MODULE_NAME = Franz; + PRODUCT_NAME = Franz; + SKIP_INSTALL = YES; + SWIFT_SWIFT3_OBJC_INFERENCE = Off; + SWIFT_VERSION = 4.0; + TARGET_NAME = franz; + }; + name = Release; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ - OBJ_2 /* Build configuration list for PBXProject "Franz" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - OBJ_3 /* Debug */, - OBJ_4 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Debug; - }; - OBJ_44 /* Build configuration list for PBXNativeTarget "FranzTests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - OBJ_45 /* Debug */, - OBJ_46 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Debug; - }; - OBJ_53 /* Build configuration list for PBXNativeTarget "Franz" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - OBJ_54 /* Debug */, - OBJ_55 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Debug; - }; - OBJ_84 /* Build configuration list for PBXAggregateTarget "FranzPackageTests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - OBJ_85 /* Debug */, - OBJ_86 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Debug; - }; + A3CE7D9A1F5A004F00FBB198 /* Build configuration list for PBXNativeTarget "DockerTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + A3CE7D981F5A004F00FBB198 /* Debug */, + A3CE7D991F5A004F00FBB198 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Debug; + }; + OBJ_2 /* Build configuration list for PBXProject "Franz" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + OBJ_3 /* Debug */, + OBJ_4 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Debug; + }; + OBJ_44 /* Build configuration list for PBXNativeTarget "FranzTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + OBJ_45 /* Debug */, + OBJ_46 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Debug; + }; + OBJ_53 /* Build configuration list for PBXNativeTarget "Franz" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + OBJ_54 /* Debug */, + OBJ_55 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Debug; + }; /* End XCConfigurationList section */ - }; - rootObject = OBJ_1 /* Project object */; + }; + rootObject = OBJ_1 /* Project object */; } diff --git a/Franz.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Franz.xcodeproj/project.xcworkspace/contents.xcworkspacedata index 21c0569..ca3329e 100644 --- a/Franz.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ b/Franz.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -2,6 +2,6 @@ + location = "group:"> diff --git a/Franz.xcodeproj/xcshareddata/xcschemes/franz-Package.xcscheme b/Franz.xcodeproj/xcshareddata/xcschemes/Franz.xcscheme similarity index 70% rename from Franz.xcodeproj/xcshareddata/xcschemes/franz-Package.xcscheme rename to Franz.xcodeproj/xcshareddata/xcschemes/Franz.xcscheme index dc0f876..dd8e9c5 100644 --- a/Franz.xcodeproj/xcshareddata/xcschemes/franz-Package.xcscheme +++ b/Franz.xcodeproj/xcshareddata/xcschemes/Franz.xcscheme @@ -27,7 +27,8 @@ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" language = "" - shouldUseLaunchSchemeArgsEnv = "YES"> + shouldUseLaunchSchemeArgsEnv = "YES" + codeCoverageEnabled = "YES"> @@ -39,7 +40,26 @@ ReferencedContainer = "container:Franz.xcodeproj"> + + + + + + + + @@ -72,6 +92,15 @@ savedToolIdentifier = "" useCustomWorkingDirectory = "NO" debugDocumentVersioning = "YES"> + + + + diff --git a/Sources/Franz/AdminAPI.swift b/Sources/Franz/AdminAPI.swift index 09b28d4..f35f056 100644 --- a/Sources/Franz/AdminAPI.swift +++ b/Sources/Franz/AdminAPI.swift @@ -17,11 +17,11 @@ class ListGroupsRequest: KafkaRequest { class ListGroupsResponse: KafkaResponse { - private var _errorCode: KafkaInt16 + private var _errorCode: Int16 private var _groups: KafkaArray var error: KafkaErrorCode? { - return KafkaErrorCode(rawValue: _errorCode.value) + return KafkaErrorCode(rawValue: _errorCode) } var groups: [String: String] { @@ -34,69 +34,55 @@ class ListGroupsResponse: KafkaResponse { return groups } - required init( bytes: inout [UInt8]) { - _errorCode = KafkaInt16(bytes: &bytes) - _groups = KafkaArray(bytes: &bytes) - super.init(bytes: &bytes) + required init(data: inout Data) { + _errorCode = Int16(data: &data) + _groups = KafkaArray(data: &data) } - lazy var length: Int = { - return self._errorCode.length + - self._groups.length + lazy var dataLength: Int = { + return self._errorCode.dataLength + + self._groups.dataLength }() lazy var data: Data = { - var data = Data(capacity: self.length) + var data = Data(capacity: self.dataLength) data.append(self._errorCode.data) data.append(self._groups.data) return data }() - - override var description: String { - return "LIST GROUPS RESPONSE:\n" + - "\tERROR CODE: \(self.error?.code ?? 0)\n" + - "\tERROR DESCRIPTION: \(self.error?.description ?? String())\n" + - "\tGROUPS(\(self._groups.length)):\n" + - _groups.description - } } -class ListedGroup: KafkaClass { - private var _groupId: KafkaString - private var _protocolType: KafkaString +class ListedGroup: KafkaType { + private var _groupId: String + private var _protocolType: String var id: String { - return _groupId.value ?? String() + return _groupId } var groupProtocolType: String { - return _protocolType.value ?? String() + return _protocolType } - required init( bytes: inout [UInt8]) { - _groupId = KafkaString(bytes: &bytes) - _protocolType = KafkaString(bytes: &bytes) + required init(data: inout Data) { + _groupId = String(data: &data) + _protocolType = String(data: &data) } - lazy var length: Int = { - return self._groupId.length + - self._protocolType.length + lazy var dataLength: Int = { + return self._groupId.dataLength + + self._protocolType.dataLength }() lazy var data: Data = { - var data = Data(capacity: self.length) + var data = Data(capacity: self.dataLength) data.append(self._groupId.data) data.append(self._protocolType.data) return data }() - - lazy var description: String = { - return "\t\tGROUP ID(\(self._groupId.length)): \(self.id) => \(self._groupId.data)\n" + - "\t\tPROTOCOL TYPE(\(self._protocolType.length)): \(self.groupProtocolType) => \(self._protocolType.data)\n" - }() } @@ -116,34 +102,30 @@ class DescribeGroupsRequest: KafkaRequest { } -class DescribeGroupsRequestMessage: KafkaClass { - private var _groupIds: KafkaArray +class DescribeGroupsRequestMessage: KafkaType { + private var _groupIds: KafkaArray init(groupIds: [String]) { - var values = [KafkaString]() + var values = [String]() for value in groupIds { - values.append(KafkaString(value: value)) + values.append(value) } - _groupIds = KafkaArray(values: values) + _groupIds = KafkaArray(values) } - required init( bytes: inout [UInt8]) { - _groupIds = KafkaArray(bytes: &bytes) + required init(data: inout Data) { + _groupIds = KafkaArray(data: &data) } - lazy var length: Int = { - return self._groupIds.length + lazy var dataLength: Int = { + return self._groupIds.dataLength }() lazy var data: Data = { - var data = Data(capacity: self.length) + var data = Data(capacity: self.dataLength) data.append(self._groupIds.data) return data }() - - lazy var description: String = { - return "GROUP ID(\(self._groupIds.length)): \(self._groupIds.values) => \(self._groupIds.data)" - }() } @@ -154,161 +136,125 @@ class DescribeGroupsResponse: KafkaResponse { return _groups.values } - required init(bytes: inout [UInt8]) { - _groups = KafkaArray(bytes: &bytes) - super.init(bytes: &bytes) + required init(data: inout Data) { + _groups = KafkaArray(data: &data) } - lazy var length: Int = { - return self._groups.length + lazy var dataLength: Int = { + return self._groups.dataLength }() lazy var data: Data = { - var data = Data(capacity: self.length) + var data = Data(capacity: self.dataLength) data.append(self._groups.data) return data }() - - override var description: String { - return "DESCRIBE GROUP RESPONSE:\n" + - "\tGROUPS:\n" + self._groups.description - } } -class GroupStateResponse: KafkaClass { - private var _errorCode: KafkaInt16 - private var _groupId: KafkaString - private var _state: KafkaString - private var _protocolType: KafkaString - private var _protocol: KafkaString +class GroupStateResponse: KafkaType { + private var _errorCode: Int16 + private var _groupId: String + private var _state: String + private var _protocolType: String + private var _protocol: String private var _members: KafkaArray var id: String? { - return _groupId.value + return _groupId } var kafkaProtocol: String? { - return _protocol.value + return _protocol } - var protocolType: GroupProtocol? { - if let type = _protocolType.value { - if type == "consumer" { - return GroupProtocol.consumer - } else { - return GroupProtocol.custom(name: type) - } - } else { - return nil - } + var protocolType: GroupProtocol { + if _protocolType == "consumer" { + return GroupProtocol.consumer + } else { + return GroupProtocol.custom(name: _protocolType) + } } var error: KafkaErrorCode? { - return KafkaErrorCode(rawValue: _errorCode.value) + return KafkaErrorCode(rawValue: _errorCode) } - var state: GroupState? { - if let state = _state.value { - return GroupState(rawValue: state) - } else { - return nil - } + var state: GroupState { + return GroupState(rawValue: _state)! } var members: [GroupMemberResponse] { return _members.values } - required init(bytes: inout [UInt8]) { - _errorCode = KafkaInt16(bytes: &bytes) - _groupId = KafkaString(bytes: &bytes) - _state = KafkaString(bytes: &bytes) - _protocolType = KafkaString(bytes: &bytes) - _protocol = KafkaString(bytes: &bytes) - _members = KafkaArray(bytes: &bytes) + required init(data: inout Data) { + _errorCode = Int16(data: &data) + _groupId = String(data: &data) + _state = String(data: &data) + _protocolType = String(data: &data) + _protocol = String(data: &data) + _members = KafkaArray(data: &data) } - lazy var length: Int = { - return self._errorCode.length + - self._groupId.length + - self._state.length + - self._protocolType.length + - self._protocol.length + - self._members.length + lazy var dataLength: Int = { + return self._errorCode.dataLength + + self._groupId.dataLength + + self._state.dataLength + + self._protocolType.dataLength + + self._protocol.dataLength + + self._members.dataLength }() lazy var data: Data = { - var data = Data(capacity: self.length) + var data = Data(capacity: self.dataLength) data.append(self._groupId.data) data.append(self._protocolType.data) return data }() - - lazy var description: String = { - return """ - ERROR CODE: \(self.error?.code ?? 0) - ERROR DESCRIPTION: \(self.error?.description ?? String()) - GROUP ID(\(self._groupId.length)): \(self.id ?? "") => \(self._groupId.data) - STATE(\(self._state.length)): \(String(describing: self.state)) => \(self._state.data) - PROTOCOL TYPE(\(self._protocolType.length)): \(self._protocolType.value ?? "nil") => \(self._protocolType.data) - PROTOCOL(\(self._protocol.length)): \(self.kafkaProtocol ?? "nil") => \(self._protocol.data) - MEMBERS(\(self._members.length)): - \(self._members.description) - """ - }() + } -class GroupMemberResponse: KafkaClass { - private var _memberId: KafkaString - private var _clientId: KafkaString - private var _clientHost: KafkaString - private var _memberMetadata: KafkaBytes - private var _memberAssignment: KafkaBytes +class GroupMemberResponse: KafkaType { + private var _memberId: String + private var _clientId: String + private var _clientHost: String + private var _memberMetadata: Data + private var _memberAssignment: Data var memberId: String { - return _memberId.value ?? String() + return _memberId } var clientId: String { - return _clientId.value ?? String() + return _clientId } var host: String { - return _clientHost.value ?? String() + return _clientHost } - required init(bytes: inout [UInt8]) { - _memberId = KafkaString(bytes: &bytes) - _clientId = KafkaString(bytes: &bytes) - _clientHost = KafkaString(bytes: &bytes) - _memberMetadata = KafkaBytes(bytes: &bytes) - _memberAssignment = KafkaBytes(bytes: &bytes) + required init(data: inout Data) { + _memberId = String(data: &data) + _clientId = String(data: &data) + _clientHost = String(data: &data) + _memberMetadata = Data(data: &data) + _memberAssignment = Data(data: &data) } - lazy var length: Int = { - return self._memberId.length + - self._clientId.length + - self._clientHost.length + - self._memberMetadata.length + - self._memberAssignment.length + lazy var dataLength: Int = { + return self._memberId.dataLength + + self._clientId.dataLength + + self._clientHost.dataLength + + self._memberMetadata.dataLength + + self._memberAssignment.dataLength }() lazy var data: Data = { - var data = Data(capacity: self.length) + var data = Data(capacity: self.dataLength) return data }() - - lazy var description: String = { - return """ - MEMBER ID: \(self.memberId) - CLIENT ID: \(self.clientId) - CLIENT HOST: \(self.host) - MEMBER METADATA: \(String(describing: self._memberMetadata.value)) - MEMBER METADATA: \(String(describing: self._memberAssignment.value)) - """ - }() } diff --git a/Sources/Franz/Assignment.swift b/Sources/Franz/Assignment.swift new file mode 100644 index 0000000..e894597 --- /dev/null +++ b/Sources/Franz/Assignment.swift @@ -0,0 +1,45 @@ +// +// Assignment.swift +// Franz +// +// Created by Luke Lau on 14/07/2017. +// + +import Foundation + +extension Cluster { + + func assignRoundRobin(members: [MemberId], partitions: [TopicName: [Partition]]) -> [MemberId: [TopicName: [PartitionId]]] { + var partitionAndTopics = partitions.reduce([(TopicName, PartitionId)](), { (total, arg1) in + let (topic, partitions) = arg1 + + return total + partitions.map { (topic, $0.id) } + }) + + var result = [MemberId: [TopicName: [PartitionId]]]() + + for member in members { + result[member] = [TopicName: [PartitionId]]() + for topic in partitions.keys { + result[member]![topic] = [PartitionId]() + } + } + + var i = 0 + while !partitionAndTopics.isEmpty { + let currentMember = members[i % members.count] + let (topic, partition) = partitionAndTopics.removeFirst() + result[currentMember]![topic]!.append(partition) + + i += 1 + } + + return result + } + + func assignRoundRobin(members: [MemberId], topics: [TopicName], completion: @escaping ([MemberId: [TopicName: [PartitionId]]]) -> Void) { + getParitions(for: topics) { partition in + completion(self.assignRoundRobin(members: members, partitions: partition)) + } + } +} diff --git a/Sources/Franz/Broker.swift b/Sources/Franz/Broker.swift index 3d9742c..a57745f 100644 --- a/Sources/Franz/Broker.swift +++ b/Sources/Franz/Broker.swift @@ -8,18 +8,62 @@ import Foundation -public enum BrokerError: Error { +enum BrokerError: Error { case noConnection case noGroupMembershipForBroker + case fetchFailed + case kafkaError(KafkaErrorCode) } +// +//protocol BrokerProtocol { +// +// //MARK: Offset API +// func getOffsets(for topic: TopicName, partition: PartitionId, clientId: String, callback: @escaping ([Offset]) -> ()) +// func getGroupCoordinator(groupId: String, clientId: String, callback: @escaping (GroupCoordinatorResponse) -> Void) +// func commitGroupOffset(groupId: String, topics: [TopicName: [PartitionId: (Offset, OffsetCommitRequest.Metadata?)]], clientId: String, callback: (() -> Void)?) +// +// //MARK: Groups API +// func join(groupId: String, subscription: [String], clientId: String, callback: ((GroupMembership) -> ())?) +// func listGroups(clientId: String, callback: ((String, String) -> ())?) +// func describeGroups(_ groupId: String, clientId: String, callback: ((String, GroupState) -> ())?) +// func syncGroup(_ groupId: String, generationId: Int32, memberId: String, topics: [TopicName: [PartitionId]], userData: Data, clientId: String, version: ApiVersion, callback: ((GroupMemberAssignment) -> ())?) +// func leaveGroup(_ groupId: String, memberId: String, clientId: String, callback: (() -> ())?) +// +// //MARK: Metadata API +// func getTopicMetadata(topics: [TopicName], clientId: String, completion: @escaping (MetadataResponse) -> Void) +// +// //MARK: Consumer API +// func fetch(_ topic: TopicName, partition: PartitionId, clientId: String, callback: @escaping ([Message]) -> ()) +// func fetch(_ topic: TopicName, partition: PartitionId, offset: Offset, clientId: String, replicaId: ReplicaId, callback: @escaping ([Message]) -> ()) +// func fetch(topics: [TopicName: [PartitionId: Offset]], clientId: String, replicaId: ReplicaId, callback: @escaping ([Message]) -> ()) +// +// func poll(topics: [TopicName: [PartitionId]], groupId: String, clientId: String, replicaId: ReplicaId, callback: @escaping (TopicName, PartitionId, Offset, [Message]) -> (), errorCallback: ((BrokerError) -> Void)?) +// func poll(topics: [TopicName: [PartitionId: Offset]], clientId: String, replicaId: ReplicaId, callback: @escaping (TopicName, PartitionId, Offset, [Message]) -> (), errorCallback: ((BrokerError) -> Void)?) +// +// //MARK: Producer API +// func send(_ topic: TopicName, partition: PartitionId, batch: MessageSet, clientId: String) +// +// func heartbeatRequest(_ groupId: String, generationId: Int32, memberId: String, clientId: String, callback: (() -> ())?) +// +// var nodeId: Int32 { get set } +// var ipv4: String { get } +// var port: Int32 { get } +//} +// +//extension BrokerProtocol { +// func commitGroupOffset(groupId: String, topics: [TopicName: [PartitionId: (Offset, OffsetCommitRequest.Metadata?)]], clientId: String) { +// commitGroupOffset(groupId: groupId, topics: topics, clientId: clientId, callback: nil) +// } +//} -class Broker: KafkaClass { +class Broker: KafkaType { + var groupMembership = [String: GroupMembership]() - private var _nodeId: KafkaInt32 - private var _host: KafkaString - private var _port: KafkaInt32 + var nodeId: Int32 + var host: String + var port: Int32 private var _readQueues = [Int32: DispatchQueue]() @@ -51,63 +95,39 @@ class Broker: KafkaClass { ) }() - private var _connection: KafkaConnection? - - var nodeId: Int32 { - get { - return _nodeId.value - } - set(newNodeId) { - _nodeId.value = newNodeId - } - } - - var host: String { - return _host.value ?? String() - } + private var _connection: Connection? var ipv4: String { - return _host.value ?? String() - } - - var port: Int32 { - return _port.value - } - - var description: String { - return "BROKER:\n\t" + - "NODE ID: \(nodeId)\n\t" + - "HOST: \(host)\n\t" + - "PORT: \(port)" + return host } init(ipv4: String, port: Int32) { - _host = KafkaString(value: ipv4) - _port = KafkaInt32(value: port) - _nodeId = KafkaInt32(value: -1) + host = ipv4 + self.port = port + nodeId = -1 } init(nodeId: Int32, host: String, port: Int32) { - self._nodeId = KafkaInt32(value: nodeId) - self._host = KafkaString(value: host) - self._port = KafkaInt32(value: port) + self.nodeId = nodeId + self.host = host + self.port = port } - required init( bytes: inout [UInt8]) { - _nodeId = KafkaInt32(bytes: &bytes) - _host = KafkaString(bytes: &bytes) - _port = KafkaInt32(bytes: &bytes) + required init(data: inout Data) { + nodeId = Int32(data: &data) + host = String(data: &data) + port = Int32(data: &data) } - var length: Int { - return _nodeId.length + _host.length + _port.length + var dataLength: Int { + return nodeId.dataLength + host.dataLength + port.dataLength } var data: Data { - return Data() + return nodeId.data + host.data + port.data } - func connect(_ clientId: String) -> KafkaConnection { + func connect(_ clientId: String) -> Connection { if _connection == nil { _connection = KafkaConnection( ipv4: ipv4, @@ -119,140 +139,96 @@ class Broker: KafkaClass { return _connection! } + + class CancelToken { + fileprivate var shouldCancel = false + + func cancel() { + shouldCancel = true + } + } - func poll( - _ topic: String, - partition: Int32, - groupId: String, - clientId: String, - replicaId: ReplicaId, - _ callback: @escaping (Int64, [Message]) -> (), - _ errorCallback: ((KafkaErrorCode) -> ())? = nil - ) throws { - if let _ = groupMembership[groupId] { - fetchGroupOffset( - groupId, - topic: topic, - partition: partition, - clientId: clientId - ) { offset in - self.poll( - topic, - partition: partition, - offset: offset, - clientId: clientId, - replicaId: replicaId, - callback, - errorCallback - ) - } - } else { - throw BrokerError.noGroupMembershipForBroker - } + func poll(topics: [TopicName: [PartitionId]], fromStart: Bool, groupId: String, clientId: String, replicaId: ReplicaId, callback: @escaping (TopicName, PartitionId, Offset, [Message]) -> (), errorCallback: ((BrokerError) -> Void)? = nil) -> CancelToken { + + let cancelToken = CancelToken() + + guard groupMembership[groupId] != nil else { + errorCallback?(.noGroupMembershipForBroker) + return cancelToken + } + getOffsets(for: topics, clientId: clientId, time: fromStart ? .earliest : .latest) { offsets in + let topicsWithOffsets = offsets.mapValues({ partitions in + partitions.mapValues({ offsets in + offsets.sorted().last + }) + .filter({ $1 != nil }) + .mapValues({ $0! }) + }) + self.poll(topics: topicsWithOffsets, clientId: clientId, replicaId: replicaId, cancelToken: cancelToken, callback: callback, errorCallback: errorCallback) + } + return cancelToken } - - func poll( - _ topic: String, - partition: Int32, - offset: Int64, - clientId: String, - replicaId: ReplicaId, - _ callback: @escaping (Int64, [Message]) -> (), - _ errorCallback: ((KafkaErrorCode) -> ())? = nil - ) { - let readQueue = getReadQueue(topic, partition: partition) - - let request = FetchRequest( - partitions: [topic: [partition: offset]], - replicaId: replicaId - ) - - connect(clientId).write(request) { bytes in - readQueue.async { - var mutableBytes = bytes - let response = FetchResponse(bytes: &mutableBytes) - for responseTopic in response.topics { - for responsePartition in responseTopic.partitions { - if let error = responsePartition.error { - if error.code == 0 { - callback( - responsePartition.offset, - responsePartition.messages - ) - - self.poll( - topic, - partition: responsePartition.partition, - offset: responsePartition.offset, - clientId: clientId, - replicaId: replicaId, - callback, - errorCallback - ) - } else { - if let errorCallback = errorCallback { - errorCallback(error) - } - } - } else { - print("Unable to parse error.") - print(response.description) - } - } - } - } + + func poll(topics: [TopicName: [PartitionId: Offset]], clientId: String, replicaId: ReplicaId, cancelToken: CancelToken? = nil, callback: @escaping (TopicName, PartitionId, Offset, [Message]) -> (), errorCallback: ((BrokerError) -> Void)? = nil) { + + let request = FetchRequest(topics: topics, replicaId: replicaId) + + connect(clientId).write(request) { data in + //Check to see if we should stop polling + if let token = cancelToken, token.shouldCancel { + return + } + + var mutableData = data + let response = FetchResponse(data: &mutableData) + + var topicsWithNewOffsets = topics + + for responseTopic in response.topics { + for responsePartition in responseTopic.partitions { + + //Update offset for that partition + topicsWithNewOffsets[responseTopic.topicName]?[responsePartition.partition] = responsePartition.offset + + if let error = responsePartition.error { + if error.code == 0 { + callback( + responseTopic.topicName, + responsePartition.partition, + responsePartition.offset, + responsePartition.messages + ) + } else { + errorCallback?(.kafkaError(error)) + } + } else { + errorCallback?(.fetchFailed) + } + } + } + + //Poll again with new offsets + self.poll(topics: topicsWithNewOffsets, clientId: clientId, replicaId: replicaId, cancelToken: cancelToken, callback: callback, errorCallback: errorCallback) } } + + func fetch(_ topic: TopicName, partition: PartitionId, clientId: String, callback: @escaping ([Message]) -> ()) { + fetch(topic, partition: partition, offset: 0, clientId: clientId, replicaId: ReplicaId.none, callback: callback) + } - func fetch( - _ topic: String, - partition: Int32, - offset: Int64, - clientId: String, - replicaId: ReplicaId, - callback: @escaping ([Message]) -> () - ) { - fetch( - [topic: [partition: offset]], - clientId: clientId, - replicaId: replicaId, - callback: callback - ) - } - - func fetch( - _ topic: String, - partition: Int32, - clientId: String, - callback: @escaping ([Message]) -> () - ) { - fetch( - topic, - partition: partition, - offset: 0, - clientId: clientId, - replicaId: ReplicaId.none, - callback: callback - ) + func fetch(_ topic: TopicName, partition: PartitionId, offset: Offset, clientId: String, replicaId: ReplicaId, callback: @escaping ([Message]) -> ()) { + fetch(topics: [topic: [partition: offset]], clientId: clientId, replicaId: replicaId, callback: callback) } - func fetch( - _ topics: [String: [Int32:Int64]], - clientId: String, - replicaId: ReplicaId, - callback: @escaping ([Message]) -> () - ) { + func fetch(topics: [TopicName: [PartitionId: Offset]], clientId: String, replicaId: ReplicaId, callback: @escaping ([Message]) -> ()) { let readQueue = getReadQueue("topics", partition: 0) + + let request = FetchRequest(topics: topics, replicaId: replicaId) - let request = FetchRequest( - partitions: topics, - replicaId: replicaId - ) - - connect(clientId).write(request) { bytes in + connect(clientId).write(request) { data in readQueue.async { - var mutableBytes = bytes - let response = FetchResponse(bytes: &mutableBytes) + var mutableData = data + let response = FetchResponse(data: &mutableData) for responseTopic in response.topics { for responsePartition in responseTopic.partitions { if let error = responsePartition.error { @@ -260,11 +236,9 @@ class Broker: KafkaClass { callback(responsePartition.messages) } else { print("ERROR: \(error.description)") - print(response.description) } } else { print("Unable to parse error.") - print(response.description) } } } @@ -272,126 +246,107 @@ class Broker: KafkaClass { } } - func send(_ topic: String, partition: Int32, batch: MessageSet, clientId: String) { + func send(_ topic: TopicName, partition: PartitionId, batch: MessageSet, clientId: String) { let request = ProduceRequest(values: [topic: [partition: batch]]) - connect(clientId).write(request) - } - - func commitGroupOffset( - _ groupId: String, - topic: String, - partition: Int32, - offset: Int64, - metadata: String?, - clientId: String, - _ callback: (() -> ())? = nil, - _ errorCallback: ((KafkaErrorCode) -> ())? = nil - ) { - if let groupMembership = self.groupMembership[groupId] { - let request = OffsetCommitRequest( - consumerGroupId: groupId, - generationId: groupMembership.group.generationId, - consumerId: groupMembership.memberId, - topics: [topic: [partition: (offset, metadata)]] - ) - - connect(clientId).write(request) { bytes in - self._metadataReadQueue.async { - var mutableBytes = bytes - let response = OffsetCommitResponse(bytes: &mutableBytes) - for responseTopic in response.topics { - for responsePartition in responseTopic.partitions { - if let error = responsePartition.error { - if error.code != 0 { - errorCallback?(error) - } else { - callback?() - } - } else { - print("Unable to parse error.") - print(response.description) - } - } - } - } - } - } + connect(clientId).write(request) } + + func commitGroupOffset(groupId: String, topics: [TopicName: [PartitionId: (Offset, OffsetMetadata?)]], clientId: String, callback: (() -> Void)? = nil) { + guard let groupMembership = self.groupMembership[groupId] else { return } + + let request = OffsetCommitRequest(consumerGroupId: groupId, generationId: groupMembership.group.generationId, consumerId: groupMembership.memberId, topics: topics) + + connect(clientId).write(request) { data in + self._metadataReadQueue.async { + var mutableData = data + let response = OffsetCommitResponse(data: &mutableData) + for responseTopic in response.topics { + for responsePartition in responseTopic.partitions { + if let error = responsePartition.error { + if error.code == 0 { + callback?() + } + else { + print("Error with offset commit \(error)") + } + } else { + print("Unable to parse error.") + } + } + } + } + } + } - func fetchGroupOffset( - _ groupId: String, - topic: String, - partition: Int32, - clientId: String, - callback: @escaping (Int64) -> () - ) { + func fetchOffsets(groupId: String, topics: [TopicName: [PartitionId]], clientId: String, callback: @escaping ([TopicName: [PartitionId: Offset]]) -> ()) { if let _ = self.groupMembership[groupId] { let request = OffsetFetchRequest( consumerGroupId: groupId, - topics: [topic: [partition]] + topics: topics ) - connect(clientId).write(request) { bytes in + connect(clientId).write(request) { data in self._metadataReadQueue.async { - var mutableBytes = bytes - let response = OffsetFetchResponse(bytes: &mutableBytes) - for responseTopic in response.topics { - for responsePartition in responseTopic.partitions { - if let error = responsePartition.error { - if error.code == 0 { - callback(responsePartition.offset) - } else { - print("ERROR: \(error.description)") - print(response.description) - } - } else { - print("Unable to parse error.") - print(response.description) - } - } - } + var mutableData = data + let response = OffsetFetchResponse(data: &mutableData) + + var offsets = [TopicName: [PartitionId: Offset]]() + + for topicPartitions in response.topics { + var entry = [PartitionId: Offset]() + for partitionOffsets in topicPartitions.partitions { + entry[partitionOffsets.partition] = partitionOffsets.offset + } + offsets[topicPartitions.topic] = entry + } + + callback(offsets) } } } } - func getOffsets( - _ topic: String, - partition: Int32, - clientId: String, - callback: @escaping ([Int64]) -> () - ) { - let request = OffsetRequest(topic: topic, partitions: [partition]) - connect(clientId).write(request) { bytes in + func getOffsets(for topics: [TopicName: [PartitionId]], clientId: String, time: TimeOffset = .latest, callback: @escaping ([TopicName: [PartitionId: [Offset]]]) -> ()) { + let request = OffsetRequest(topics: topics, time: time) + connect(clientId).write(request) { data in self._metadataReadQueue.async { - var mutableBytes = bytes - let response = OffsetResponse(bytes: &mutableBytes) - for topicalPartitionedOffsets in response.topicalPartitionedOffsets { - for (_, partitionedOffsets) in topicalPartitionedOffsets.partitionedOffsets { - if let error = partitionedOffsets.error { - if error.code == 0 { - callback(partitionedOffsets.offsets) - } else { - print("ERROR: \(error.description)") - print(response.description) - } - } else { - print("Unable to parse error.") - print(response.description) - } - } - } + var mutableData = data + let response = OffsetResponse(data: &mutableData) + + if let error = response.topicalPartitionedOffsets.flatMap({ $0.partitionedOffsets.flatMap { $0.value.error } }) + .filter({ $0 != .noError }) + .first { + print("ERROR: \(error.description)") + return + } + + let topicsWithOffsets = Dictionary(uniqueKeysWithValues: response.topicalPartitionedOffsets.map { topic -> (TopicName, [PartitionId : [Offset]]) in + let partitions = Dictionary(uniqueKeysWithValues: topic.partitionedOffsets.map { (partitionId, offsets) in + (partitionId, offsets.offsets) + }) + return (topic.topicName, partitions) + }) + + callback(topicsWithOffsets) } } } + + func getGroupCoordinator(groupId: String, clientId: String, callback: @escaping (GroupCoordinatorResponse) -> Void) { + let request = GroupCoordinatorRequest(id: groupId) + connect(clientId).write(request) { data in + var mutableData = data + callback(GroupCoordinatorResponse(data: &mutableData)) + } + } - func listGroups(_ clientId: String, callback: ((String, String) -> ())? = nil) { + func listGroups(clientId: String, callback: ((String, String) -> ())? = nil) { let listGroupsRequest = ListGroupsRequest() - connect(clientId).write(listGroupsRequest) { bytes in + connect(clientId).write(listGroupsRequest) { data in self._metadataReadQueue.async { - var mutableBytes = bytes - let response = ListGroupsResponse(bytes: &mutableBytes) + var mutableData = data + let response = ListGroupsResponse(data: &mutableData) if let error = response.error { if error.code == 0 { for (groupId, groupProtocol) in response.groups { @@ -401,11 +356,9 @@ class Broker: KafkaClass { } } else { print("ERROR: \(error.description)") - print(response.description) } } else { print("Unable to parse error.") - print(response.description) } } } @@ -417,37 +370,28 @@ class Broker: KafkaClass { callback: ((String, GroupState) -> ())? = nil ) { let describeGroupRequest = DescribeGroupsRequest(id: groupId) - connect(clientId).write(describeGroupRequest) { bytes in + connect(clientId).write(describeGroupRequest) { data in self._metadataReadQueue.async { - var mutableBytes = bytes - let response = DescribeGroupsResponse(bytes: &mutableBytes) + var mutableData = data + let response = DescribeGroupsResponse(data: &mutableData) for groupState in response.states { if let error = groupState.error { if error.code == 0 { - if let describeGroupCallback = callback { - if let id = groupState.id, let state = groupState.state { - describeGroupCallback(id, state) - } + if let describeGroupCallback = callback, let id = groupState.id { + describeGroupCallback(id, groupState.state) } } else { print("ERROR: \(error.description)") - print(response.description) } } else { print("Unable to parse error.") - print(response.description) } } } } } - func joinGroup( - _ groupId: String, - subscription: [String], - clientId: String, - callback: ((GroupMembership) -> ())? = nil - ) { + func join(groupId: String, subscription: [String], clientId: String, callback: ((GroupMembership) -> ())? = nil) { let metadata = ConsumerGroupMetadata(subscription: subscription) let request = GroupMembershipRequest( @@ -455,10 +399,10 @@ class Broker: KafkaClass { metadata: [AssignmentStrategy.RoundRobin: metadata] ) - connect(clientId).write(request) { bytes in + connect(clientId).write(request) { data in self._groupCoordinationQueue.async { - var mutableBytes = bytes - let response = JoinGroupResponse(bytes: &mutableBytes) + var mutableData = data + let response = JoinGroupResponse(data: &mutableData) //print(response.description) if let error = response.error { @@ -473,7 +417,8 @@ class Broker: KafkaClass { let groupMembership = GroupMembership( group: group, - memberId: response.memberId + memberId: response.memberId, + members: response.members ) self.groupMembership[groupId] = groupMembership @@ -483,11 +428,9 @@ class Broker: KafkaClass { } } else { print("ERROR: \(error.description)") - print(response.description) } } else { print("Unable to parse error.") - print(response.description) } } } @@ -497,41 +440,39 @@ class Broker: KafkaClass { _ groupId: String, generationId: Int32, memberId: String, - topics: [String: [Int32]], + topics: [TopicName: [PartitionId]], userData: Data, clientId: String, version: ApiVersion = ApiVersion.defaultVersion, - callback: (() -> ())? = nil + callback: ((GroupMemberAssignment) -> ())? = nil ) { - let groupAssignmentMetadata = [GroupMemberAssignment( + let groupAssignmentMetadata = GroupMemberAssignment( topics: topics, userData: userData, version: version - )] + ) let request = SyncGroupRequest( groupId: groupId, generationId: generationId, memberId: memberId, - groupAssignment: groupAssignmentMetadata - ) - - connect(clientId).write(request) { bytes in + groupAssignment: [memberId: groupAssignmentMetadata] + ) + + connect(clientId).write(request) { data in self._groupCoordinationQueue.async { - var mutableBytes = bytes - let response = SyncGroupResponse(bytes: &mutableBytes) + var mutableData = data + let response = SyncGroupResponse(data: &mutableData) if let error = response.error { if error.code == 0 { if let syncGroupCallback = callback { - syncGroupCallback() + syncGroupCallback(response.memberAssignment) } } else { print("ERROR: \(error.description)") - print(response.description) } } else { print("Unable to parse error.") - print(response.description) } } } @@ -545,10 +486,10 @@ class Broker: KafkaClass { ) { let request = LeaveGroupRequest(groupId: groupId, memberId: memberId) - connect(clientId).write(request) { bytes in + connect(clientId).write(request) { data in self._groupCoordinationQueue.async { - var mutableBytes = bytes - let response = LeaveGroupResponse(bytes: &mutableBytes) + var mutableData = data + let response = LeaveGroupResponse(data: &mutableData) if let error = response.error { if error.code == 0 { if let leaveGroupCallback = callback { @@ -556,11 +497,9 @@ class Broker: KafkaClass { } } else { print("ERROR: \(error.description)") - print(response.description) } } else { print("Unable to parse error.") - print(response.description) } } } @@ -579,27 +518,36 @@ class Broker: KafkaClass { memberId: memberId ) - connect(clientId).write(request) { bytes in + connect(clientId).write(request) { data in self._groupCoordinationQueue.async { - var mutableBytes = bytes - let response = HeartbeatResponse(bytes: &mutableBytes) + var mutableData = data + let response = HeartbeatResponse(data: &mutableData) //print(response.description) if let error = response.error { - if error.code == 0 { - if let heartbeatCallback = callback { - heartbeatCallback() - } - } else { - print("ERROR: \(error.description)") - print(response.description) - } + switch error { + case .noError: + callback?() + //TODO: rejoin the group + //case .rebalanceInProgressCode: + default: + print("ERROR: \(error.description)") + } } else { print("Unable to process error.") - print(response.description) } } } } + + func getTopicMetadata(topics: [TopicName] = [], clientId: String, completion: @escaping (MetadataResponse) -> Void) { + let topicMetadataRequest = TopicMetadataRequest(topics: topics) + + connect(clientId).write(topicMetadataRequest) { data in + var mutableData = data + + completion(MetadataResponse(data: &mutableData)) + } + } private func getReadQueue(_ topic: String, partition: Int32) -> DispatchQueue { var readQueue: DispatchQueue diff --git a/Sources/Franz/Cluster.swift b/Sources/Franz/Cluster.swift index 038f338..6bebe45 100644 --- a/Sources/Franz/Cluster.swift +++ b/Sources/Franz/Cluster.swift @@ -47,6 +47,19 @@ open class Cluster { ) }() + ///Used for swapping in fake brokers during testing + internal var brokers: [Broker] { + get { + return Array(_brokers.values) + } + set { + _brokers = [String: Broker]() + for broker in newValue { + _brokers["\(broker.ipv4):\(broker.port)"] = broker + } + } + } + /* Initialize brokers @@ -120,7 +133,7 @@ open class Cluster { - Throws ClusterError.NoBatchForTopicPartition */ open func sendBatch(_ topic: String, partition: Int32) throws { - if let topicBatch = _batches[topic], let partitionBatch = topicBatch[partition] { + if let topicBatch = _batches[topic], let partitionBatch = topicBatch[partition] { findTopicLeader(topic, partition: partition, { leader in leader.send( topic, @@ -143,12 +156,10 @@ open class Cluster { - Parameter callback: */ open func listTopics(_ callback: @escaping ([Topic]) -> ()) { - doAdminRequest(TopicMetadataRequest()) { bytes in - var mutableBytes = bytes - let response = MetadataResponse(bytes: &mutableBytes) + _brokers.first?.value.getTopicMetadata(clientId: clientId) { response in var topics = [Topic]() for (name, topic) in response.topics { - var partitions = [Int32]() + var partitions = [PartitionId]() for (partition, _) in topic.partitions { partitions.append(partition) } @@ -171,11 +182,9 @@ open class Cluster { callback: @escaping ([Int64]) -> () ) { findTopicLeader(topic, partition: partition, { leader in - leader.getOffsets( - topic, - partition: partition, + leader.getOffsets(for: [topic: [partition]], clientId: self.clientId, - callback: callback + callback: { callback($0[topic]![partition]!) } ) }, { error in print(error) @@ -221,7 +230,7 @@ open class Cluster { open func listGroups(_ callback: @escaping (String, String) -> ()) { for (_, broker) in _brokers { dispatchQueue.async { - broker.listGroups(self.clientId) { a, b in + broker.listGroups(clientId: self.clientId) { a, b in callback(a, b) } } @@ -237,7 +246,8 @@ open class Cluster { - Returns: an uninitialized Simple Consumer */ - open func getSimpleConsumer( + @available(*, deprecated, message: "Use getConsumer instead") + public func getSimpleConsumer( _ topic: String, partition: Int32, delegate: ConsumerDelegate @@ -270,7 +280,8 @@ open class Cluster { - Returns: an unitialized HighLevelConsumer */ - open func getHighLevelConsumer( + @available(*, deprecated, message: "Use getConsumer instead") + public func getHighLevelConsumer( _ topic: String, partition: Int32, groupId: String, @@ -283,15 +294,17 @@ open class Cluster { delegate: delegate ) - joinGroup(groupId, topic: topic, { broker, membership in + joinGroup(id: groupId, topics: [topic], callback: { broker, membership in consumer.broker = broker consumer.membership = membership - + membership.group.getState { groupId, state in switch state { case .AwaitingSync: - membership.sync([topic: [partition]], data: Data()) { - consumer.delegate.consumerIsReady(consumer) + self.assignRoundRobin(members: membership.members.map { $0.memberId }, topics: [topic]) { assignments in + membership.sync(assignments[membership.memberId]!, data: Data()) { + consumer.delegate.consumerIsReady(consumer) + } } case .Stable: consumer.delegate.consumerIsReady(consumer) @@ -299,21 +312,53 @@ open class Cluster { print("State of Group is: \(state)") } } - }, { error in + }, error: { error in consumer.delegate.topicPartitionLeaderNotFound?(topic, partition: partition) }) return consumer } - internal func joinGroup( - _ id: String, - topic: String, - _ callback: @escaping (Broker, GroupMembership) -> (), - _ error: (KafkaErrorCode) -> () + /** + Initialize a Consumer that can be used to listen for messages. + + - parameters: + - topics: The list of topics that the consumer should be subscribed to. + - groupId: The id of the consumer group that the consumer group should belong to. + + - returns: The Consumer object that can then listen for messages. + + - seealso: `Consumer` + */ + public func getConsumer(topics: [TopicName], groupId: String) -> Consumer { + let consumer = Consumer(cluster: self, groupId: groupId) + DispatchQueue(label: "FranzConsumerGetQueue").async { + self.joinGroup(id: groupId, topics: topics, callback: { broker, membership in + consumer.broker = broker + consumer.membership = membership + membership.group.getState { groupId, state in + if state == GroupState.AwaitingSync { + self.assignRoundRobin(members: membership.members.map { $0.memberId }, topics: topics) { assignments in + membership.sync(assignments[membership.memberId]!, data: Data()) { + consumer.joinedGroupSemaphore.signal() + } + } + } + if state == .Empty { + print("Group shouldn't be empty") + } + } + }, error: { error in + + }) + } + return consumer + } + + internal func joinGroup(id: String, topics: [TopicName], callback: @escaping (Broker, GroupMembership) -> (), error: (KafkaErrorCode) -> () ) { - getGroupCoordinator(id) { broker in - broker.joinGroup(id, subscription: [topic], clientId: self.clientId) { groupMembership in + getGroupCoordinator(groupId: id) { broker in + broker.join(groupId: id, subscription: topics, clientId: self.clientId) { groupMembership in callback(broker, groupMembership) } } @@ -325,60 +370,54 @@ open class Cluster { _ callback: @escaping (Broker) -> (), _ error: @escaping (Error) -> () ) { - var dispatchBlocks = [()->()]() - - for (_, broker) in _brokers { - let dispatchBlock = DispatchWorkItem(qos: .unspecified, flags: []) { - let connection = broker.connect(self.clientId) - let topicMetadataRequest = TopicMetadataRequest(topic: topic) - //print(topicMetadataRequest.description) - connection.write(topicMetadataRequest) { bytes in - var mutableBytes = bytes - let response = MetadataResponse(bytes: &mutableBytes) - if let topicObj = response.topics[topic] { - if let partitionObj = topicObj.partitions[partition] { - if partitionObj.leader == -1 { - error(ClusterError.leaderNotFound(topic: topic, partition: partition)) + var dispatchBlocks = [()->()]() + + for (_, broker) in _brokers { + let dispatchBlock = DispatchWorkItem(qos: .unspecified, flags: []) { + + var handleGetTopicMetadata: ((MetadataResponse) -> Void)! + handleGetTopicMetadata = { response in + + if let topicObj = response.topics[topic] { + + if topicObj.error == .leaderNotAvailable { + let retryTopics = response.topics.filter { key, val in val.error == .leaderNotAvailable }.flatMap { $1.name } + sleep(1) + broker.getTopicMetadata(topics: retryTopics, clientId: self.clientId, completion: handleGetTopicMetadata) + return + } + + if let partitionObj = topicObj.partitions[partition] { + if partitionObj.leader == -1 { + error(ClusterError.leaderNotFound(topic: topic, partition: partition)) + return + } else if let leader = response.brokers[partitionObj.leader] { + if let broker = self._brokers["\(leader.host):\(leader.port)"] { + broker.nodeId = leader.nodeId + callback(broker) return - } else if let leader = response.brokers[partitionObj.leader] { - if let broker = self._brokers["\(leader.host):\(leader.port)"] { - broker.nodeId = leader.nodeId - callback(broker) - return - } else { - self._brokers["\(leader.host):\(leader.port)"] = leader - callback(leader) - return - } } else { - error(ClusterError.noLeaderFoundInResponseData) + self._brokers["\(leader.host):\(leader.port)"] = leader + callback(leader) + return } } else { - error(ClusterError.noPartitionFoundInCluster(partition: partition)) + error(ClusterError.noLeaderFoundInResponseData) } } else { - error(ClusterError.noTopicFoundInCluster(topic: topic)) - } - - if dispatchBlocks.count > 0 { - self.dispatchQueue.async(execute: dispatchBlocks.removeFirst()) + error(ClusterError.noPartitionFoundInCluster(partition: partition)) } + } else { + error(ClusterError.noTopicFoundInCluster(topic: topic)) } } - dispatchBlocks.append(dispatchBlock.perform) - } - - if dispatchBlocks.count > 0 { - dispatchQueue.async(execute: dispatchBlocks.removeFirst()) - } - } - - private func doAdminRequest(_ request: KafkaRequest, _ callback: @escaping ([UInt8]) -> ()) { - var dispatchBlocks = [()->()]() - for (_, broker) in _brokers { - let dispatchBlock = DispatchWorkItem(qos: .unspecified, flags: []) { - let connection = broker.connect(self.clientId) - connection.write(request, callback: callback) + + broker.getTopicMetadata(topics: [topic], clientId: self.clientId, completion: handleGetTopicMetadata) + + if dispatchBlocks.count > 0 { + self.dispatchQueue.async(execute: dispatchBlocks.removeFirst()) + } + } dispatchBlocks.append(dispatchBlock.perform) } @@ -387,16 +426,19 @@ open class Cluster { dispatchQueue.async(execute: dispatchBlocks.removeFirst()) } } - - private func getGroupCoordinator(_ id: String, callback: @escaping (Broker) -> ()) { - doAdminRequest(GroupCoordinatorRequest(id: id)) { bytes in - var mutableBytes = bytes - let response = GroupCoordinatorResponse(bytes: &mutableBytes) - //print(response.description) - + + func getGroupCoordinator(groupId: String, callback: @escaping (Broker) -> Void) { + _brokers.first?.value.getGroupCoordinator(groupId: groupId, clientId: clientId) { response in let host = response.host let port = response.port + //Retry if broker isn't available + if response.error == KafkaErrorCode.groupCoordinatorNotAvailableCode { + sleep(1) + self.getGroupCoordinator(groupId: groupId, callback: callback) + return + } + if let broker = self._brokers["\(host):\(port)"] { callback(broker) } else { @@ -404,4 +446,11 @@ open class Cluster { } } } + + func getParitions(for topics: [TopicName], completion: @escaping ([TopicName: [Partition]]) -> Void) { + _brokers.first?.value.getTopicMetadata(topics: topics, clientId: clientId) { response in + let partitions = response.topics.mapValues { $0.partitions.values.map({ $0 }) } + completion(partitions) + } + } } diff --git a/Sources/Franz/Connection.swift b/Sources/Franz/Connection.swift index 8aec935..9b085f7 100644 --- a/Sources/Franz/Connection.swift +++ b/Sources/Franz/Connection.swift @@ -8,7 +8,7 @@ import Foundation -typealias RequestCallback = ([UInt8]) -> () +typealias RequestCallback = (Data) -> Void enum ConnectionError: Error { case unableToOpenConnection @@ -29,7 +29,63 @@ enum ConnectionError: Error { case bytesNoLongerAvailable } -class KafkaConnection: NSObject, StreamDelegate { + +extension Stream.Event { + var description: String { + switch self { + case []: + return "None" + case .openCompleted: + return "Open Completed" + case .hasBytesAvailable: + return "Has Bytes Available" + case .hasSpaceAvailable: + return "Has Space Available" + case .errorOccurred: + return "Error Occurred" + case .endEncountered: + return "End Encountered" + default: + return "" + } + } +} + +extension Stream.Status { + var description: String { + switch self { + case .notOpen: + return "Not Open" + case .opening: + return "Opening" + case .open: + return "Open" + case .reading: + return "Reading" + case .writing: + return "Writing" + case .atEnd: + return "End" + case .closed: + return "Closed" + case .error: + return "Error" + } + } +} + +internal protocol Connection { + init(ipv4: String, port: Int32, broker: Broker, clientId: String) + func write(_ request: KafkaRequest, callback: RequestCallback?) +} + +extension Connection { + func write(_ request: KafkaRequest, callback: RequestCallback? = nil) { + write(request, callback: callback) + } +} + +class KafkaConnection: NSObject, Connection, StreamDelegate { private var ipv4: String @@ -57,7 +113,7 @@ class KafkaConnection: NSObject, StreamDelegate { private var _outputStreamQueue: DispatchQueue private var _writeRequestBlocks = [()->()]() - init(ipv4: String, port: Int32, broker: Broker, clientId: String) { + required init(ipv4: String, port: Int32, broker: Broker, clientId: String) { self.ipv4 = ipv4 self.clientId = clientId self._broker = broker @@ -83,21 +139,26 @@ class KafkaConnection: NSObject, StreamDelegate { inputStream = readStream?.takeUnretainedValue() outputStream = writeStream?.takeUnretainedValue() + + DispatchQueue(label: "FranzConnectionQueue").async { + self.inputStream?.delegate = self + self.inputStream?.schedule( + in: RunLoop.current, + forMode: RunLoopMode.defaultRunLoopMode + ) + + self.outputStream?.delegate = self + self.outputStream?.schedule( + in: RunLoop.current, + forMode: RunLoopMode.defaultRunLoopMode + ) + + self.inputStream?.open() + self.outputStream?.open() + + RunLoop.current.run() + } - self.inputStream?.delegate = self - self.inputStream?.schedule( - in: RunLoop.main, - forMode: RunLoopMode.defaultRunLoopMode - ) - - self.outputStream?.delegate = self - self.outputStream?.schedule( - in: RunLoop.main, - forMode: RunLoopMode.defaultRunLoopMode - ) - - self.inputStream?.open() - self.outputStream?.open() } private func read(_ timeout: Double = 3000) { @@ -116,9 +177,8 @@ class KafkaConnection: NSObject, StreamDelegate { var buffer = [UInt8](repeating: 0, count: Int(size)) let bytesInBuffer = inputStream.read(&buffer, maxLength: Int(size)) //print("\t\tReading Bytes") - buffer = buffer.slice(0, length: bytesInBuffer) //print("BUFFER(\(size)): \(buffer)") - bytes += buffer + bytes += buffer.prefix(upTo: Int(bytesInBuffer)) } let currentTime = Date().timeIntervalSince1970 @@ -131,7 +191,8 @@ class KafkaConnection: NSObject, StreamDelegate { } if let callback = self._requestCallbacks[correlationId] { - callback(bytes) + self._requestCallbacks.removeValue(forKey: correlationId) + callback(Data(bytes: bytes)) } else { print( "Unable to find reuqest callback for " + @@ -156,7 +217,7 @@ class KafkaConnection: NSObject, StreamDelegate { } func write(_ request: KafkaRequest, callback: RequestCallback? = nil) { - request.clientId = KafkaString(value: clientId) + request.clientId = clientId //print("Write Block Added") if let requestCallback = callback { _requestCallbacks[request.correlationId] = requestCallback @@ -167,10 +228,14 @@ class KafkaConnection: NSObject, StreamDelegate { if stream.hasSpaceAvailable { let data = request.data //print("Data: \(data)") - let bytesPtr = (data as NSData).bytes - let bytes = bytesPtr.assumingMemoryBound(to: UInt8.self) - //print("Writing to Output Stream: \(data.length)") - stream.write(bytes, maxLength: data.count) + + data.withUnsafeBytes { (bytes: UnsafePointer) -> Void in + stream.write(bytes, maxLength: data.count) + } +// let bytesPtr = (data as NSData).bytes +// let bytes = bytesPtr.assumingMemoryBound(to: UInt8.self) +// //print("Writing to Output Stream: \(data.length)") +// stream.write(bytes, maxLength: data.count) //print("\tReleasing Output Stream Write(\(data.length))") } else { print("No Space Available for Writing") @@ -198,14 +263,20 @@ class KafkaConnection: NSObject, StreamDelegate { } else { throw ConnectionError.bytesNoLongerAvailable } - - let sizeBytes = buffer.slice(0, length: Int(responseLengthSize)) - let responseLengthSizeInclusive = Int32(bytes: sizeBytes) + let sizeBytes = buffer.prefix(upTo: Int(responseLengthSize)) + buffer.removeFirst(Int(responseLengthSize)) + + var sizeData = Data(bytes: sizeBytes) + let responseLengthSizeInclusive = Int32(data: &sizeData) if responseLengthSizeInclusive > 4 { + let correlationIdSizeBytes = buffer.prefix(upTo: Int(responseCorrelationIdSize)) + buffer.removeFirst(Int(responseCorrelationIdSize)) + + var correlationIdSizeData = Data(bytes: correlationIdSizeBytes) return ( responseLengthSizeInclusive - responseLengthSize, - Int32(bytes: buffer.slice(0, length: Int(responseCorrelationIdSize))) + Int32(data: &correlationIdSizeData) ) } else if responseLengthSizeInclusive == 0 { throw ConnectionError.zeroLengthResponse diff --git a/Sources/Franz/Consumer.swift b/Sources/Franz/Consumer.swift index 538462c..89e5086 100644 --- a/Sources/Franz/Consumer.swift +++ b/Sources/Franz/Consumer.swift @@ -3,338 +3,100 @@ // Pods // // Created by Kellan Cummings on 2/5/16. -//y +// // import Foundation -/** - Base consumer delegate. Used by SimpleConsumer. -*/ -@objc public protocol ConsumerDelegate { - /** - Called when the consumer has consumed a new Message - - - Parameter message: the returned message - - Parameter offset: the message offset - */ - func consumerDidReturnMessage(_ message: Message, offset: Int64) - - /** - Called when a fetch request has failed and cannot be retried. - - - Parameter topic: the topic requested from the server - - Parameter partition: the partition requested from the server - - Parameter offset: the last message offset requested from server - - Parameter errorId: the error Id returned from the server - - Parameter errorDescription: a description of the error returned from the server - */ - @objc optional func fetchDidFail( - _ topic: String, - partition: Int32, - errorId: Int16, - errorDescription: String - ) - - /** - Called when a fetch has failed. Gives client the chance to retry before shutting down. - - - Parameter topic: the topic requested from the server - - Parameter partition: the partition requested from the server - - Parameter offset: the last message offset requested from server - - Parameter errorId: the error Id returned from the server - - Parameter errorDescription: a description of the error returned from the server - - - Returns: true if broker should attempt to retry request, false if not - */ - @objc optional func shouldRetryFailedFetch( - _ topic: String, - partition: Int32, - errorId: Int16, - errorDescription: String - ) -> Bool - - /** - Called when the Consumer is ready to starting issuing pull requests. - - - Parameter consumer: a Consumer - */ - func consumerIsReady(_ consumer: Consumer) - - /** - Called if a Leader is not found for a topic-partition - - - Parameter topic: the topic - - Parameter partition: the partition - */ - @objc optional func topicPartitionLeaderNotFound(_ topic: String, partition: Int32) -} - - -/** - High-level consumer delegate. Used by HighLevelConsumer. - */ -@objc public protocol HighLevelConsumerDelegate: ConsumerDelegate { - /** - Called after messages have been pulled for server. - - - Parameter topic: the topic - - Parameter partition: the partition - - Parameter offset: the offset - - - Returns: true if offset should be committed, false if otherwise - */ - @objc optional func shouldCommitOffset(_ topic: String, partition: Int32, offset: Int64) -> Bool - - /** - Called after messages have been pulled for server. - - - Parameter topic: the topic - - Parameter partition: the partition - - Parameter offset: the offset - - - Returns: additional metadata to send with offset commit to server - */ - @objc optional func shouldAttachOffsetMetadata(_ topic: String, partition: Int32, offset: Int64) -> String? - - /** - Called after offset has been successfully committed - - - Parameter topic: the topic - - Parameter partition: the partition - - Parameter offset: the offset - */ - @objc optional func offsetDidCommit(_ topic: String, partition: Int32, offset: Int64) - - /** - Called if offset commit has failed and cannot be retried. - - - Parameter topic: the topic - - Parameter partition: the partition - - Parameter offset: the offset - - Parameter errorId: error code id - - Parameter errorDescription: description of the error - */ - @objc optional func offsetCommitDidFail(_ topic: String, partition: Int32, offset: Int64, errorId: Int16, errorDescription: String) - - /** - Called if offset commit has failed and commit is retriable - - - Parameter topic: the topic - - Parameter partition: the partition - - Parameter offset: the offset - - Parameter errorId: error code id - - Parameter errorDescription: description of the error - - - Returns: true if offset commit should be retried, false if otherwise - */ - @objc optional func shouldRetryFailedOffsetCommit(_ topic: String, partition: Int32, offset: Int64, errorId: Int16, errorDescription: String) -> Bool -} - - -/* - Base consumer class -*/ -open class Consumer: NSObject { - internal var broker: Broker? - - fileprivate var _topic: String - fileprivate var _partition: Int32 - fileprivate var _clientId: String - - internal init(topic: String, partition: Int32, clientId: String) { - self._topic = topic - self._partition = partition - self._clientId = clientId - } -} - - -/* - Class implementing a simple consumer model. -*/ -open class SimpleConsumer: Consumer { - /** - the delegate - */ - open var delegate: ConsumerDelegate - - internal init( - topic: String, - partition: Int32, - clientId: String, - delegate: ConsumerDelegate - ) { - self.delegate = delegate - super.init(topic: topic, partition: partition, clientId: clientId) - } - - /** - Poll for messages - - Parameter offset: starting offset - */ - open func poll(_ offset: Int64) { - if let coordinator = broker { - coordinator.poll( - _topic, - partition: _partition, - offset: offset, - clientId: _clientId, - replicaId: ReplicaId.none, - { offset, messages in - for (idx, message) in messages.enumerated() { - self.delegate.consumerDidReturnMessage( - message, - offset: Int64(idx) + offset - ) - } - }, - { error in - if error.retriable { - if self.delegate.shouldRetryFailedFetch?( - self._topic, - partition: self._partition, - errorId: error.code, - errorDescription: error.description - ) != nil { - self.poll(offset) - } - } else { - self.delegate.fetchDidFail?( - self._topic, - partition: self._partition, - errorId: error.code, - errorDescription: error.description - ) - } - } - ) - } - } -} - - -/** - Class implementing a high-level consumer. Managed by a group coordinator. - The delegate is called after each fetch to -*/ -open class HighLevelConsumer: Consumer { - - /** - The Delegate - */ - open var delegate: HighLevelConsumerDelegate - - internal var membership: GroupMembership? - - internal init( - topic: String, - partition: Int32, - clientId: String, - delegate: HighLevelConsumerDelegate - ) { - self.delegate = delegate - super.init(topic: topic, partition: partition, clientId: clientId) - } - - /** - Poll for messages - */ - open func poll() { - if let groupId = membership?.group.id { - if let coordinator = broker { - do { - try coordinator.poll( - _topic, - partition: _partition, - groupId: groupId, - clientId: _clientId, - replicaId: ReplicaId.none, - { offset, messages in - for (idx, message) in messages.enumerated() { - self.delegate.consumerDidReturnMessage( - message, - offset: offset + Int64(idx) - ) - } - - if self.delegate.shouldCommitOffset != nil && self.delegate.shouldCommitOffset!( - self._topic, - partition: self._partition, - offset: offset - ) { - let metadata = self.delegate.shouldAttachOffsetMetadata?( - self._topic, - partition: self._partition, - offset: offset - ) - - coordinator.commitGroupOffset( - groupId, - topic: self._topic, - partition: self._partition, - offset: offset, - metadata: metadata, - clientId: self._clientId, - { - self.delegate.offsetDidCommit?( - self._topic, - partition: self._partition, - offset: offset - ) - }, - { error in - if error.retriable { - if self.delegate.shouldRetryFailedOffsetCommit?( - self._topic, - partition: self._partition, - offset: offset, - errorId: error.code, - errorDescription: error.description - ) != nil { - self.poll() - } - } else { - self.delegate.offsetCommitDidFail?( - self._topic, - partition: self._partition, - offset: offset, - errorId: error.code, - errorDescription: error.description - ) - } - } - ) - } - } , { error in - if error.retriable { - if self.delegate.shouldRetryFailedFetch?( - self._topic, - partition: self._partition, - errorId: error.code, - errorDescription: error.description - ) != nil { - self.poll() - } - } else { - self.delegate.fetchDidFail?( - self._topic, - partition: self._partition, - errorId: error.code, - errorDescription: error.description - ) - } - } - ) - } catch { - print("Unable to find consumer group with id '\(groupId)'") - } - } else { - print("Cannot poll without broker") - } - } else { - print("Cannot poll without group membership id") - } - } +public class Consumer { + private let cluster: Cluster + internal var broker: Broker? + internal var membership: GroupMembership? + internal let joinedGroupSemaphore = DispatchSemaphore(value: 0) + + internal init(cluster: Cluster, groupId: String) { + self.cluster = cluster + + if #available(OSX 10.12, iOS 10, tvOS 10, watchOS 3, *) { + Timer.scheduledTimer(withTimeInterval: 5, repeats: true) { _ in self.commitGroupoffsets() } + Timer.scheduledTimer(withTimeInterval: 3, repeats: true) { _ in + self.sendHeartbeat() + } + } else { + // Fallback on earlier versions + Timer.scheduledTimer(timeInterval: 5, target: self, selector: #selector(commitGroupoffsets), userInfo: nil, repeats: true) + Timer.scheduledTimer(timeInterval: 3, target: self, selector: #selector(sendHeartbeat), userInfo: nil, repeats: true) + } + } + + private let listenQueue = DispatchQueue(label: "FranzConsumerListenQueue", attributes: .concurrent) + + var offsetsToCommit = [TopicName: [PartitionId: (Offset, OffsetMetadata?)]]() + @objc private func commitGroupoffsets() { + guard let groupId = self.membership?.group.id, let broker = self.broker else { return } + broker.commitGroupOffset(groupId: groupId, topics: offsetsToCommit, clientId: cluster.clientId) + } + + @objc private func sendHeartbeat() { + guard let groupId = self.membership?.group.id, + let generationId = self.membership?.group.generationId, + let memberId = self.membership?.memberId else { + return + } + self.broker?.heartbeatRequest(groupId, generationId: generationId, memberId: memberId, clientId: cluster.clientId) + } + + /** + Returns messages from the topics that the consumer is subscribed to. + + - parameters: + - fromStart: If true the consumer will call the handler for all existing messages, and if false the consumer will only call the handler for new messages. + - handler: Called whenever a message is received, along with that message. + */ + public func listen(fromStart: Bool = true, handler: @escaping (Message) -> Void) { + if listening { + fatalError("Cannot listen multiple times from the same consumer") + } else { + listening = true + } + listenQueue.async { + self.joinedGroupSemaphore.wait() + guard let membership = self.membership, let broker = self.broker else { + return + } + + self.cluster.getParitions(for: Array(membership.group.topics)) { partitions in + let ids = partitions.reduce([TopicName: [PartitionId]](), { (result, arg1) in + let (key, value) = arg1 + var copy = result + copy[key] = value.map { $0.id } + return copy + }) + + self.cancelToken = broker.poll(topics: ids, fromStart: fromStart, groupId: membership.group.id, clientId: "test", replicaId: ReplicaId.none, callback: { topic, partitionId, offset, messages in + messages.forEach(handler) + + if var topicOffsets = self.offsetsToCommit[topic] { + topicOffsets[partitionId] = (offset, nil) + } else { + self.offsetsToCommit[topic] = [partitionId: (offset, nil)] + } + }, errorCallback: { error in + print("Error polling: \(error.localizedDescription)") + }) + } + } + } + + private var cancelToken: Broker.CancelToken? + + private var listening = false + + /** + Stops listening for incoming messages if `listen` was called. + */ + public func stop() { + cancelToken?.cancel() + listening = false + } } diff --git a/Sources/Franz/Enumerations.swift b/Sources/Franz/Enumerations.swift index 9215e17..3fbb309 100644 --- a/Sources/Franz/Enumerations.swift +++ b/Sources/Franz/Enumerations.swift @@ -327,6 +327,22 @@ public enum GroupState: String { Returned when there are no active members and group state has been cleaned up. */ case Down = "Down" + + /** + Group has no more members and its metadata is being removed + */ + case Dead = "Dead" + + /** + Group has no more members, but lingers until all offsets have expired. This state + also represents groups which use Kafka only for offset commits and have no members. + */ + case Empty = "Empty" + + /** + Group is preparing to rebalance + */ + case PreparingRebalance = "PreparingRebalance" } /** diff --git a/Sources/Franz/Extensions.swift b/Sources/Franz/Extensions.swift deleted file mode 100644 index 7324c0a..0000000 --- a/Sources/Franz/Extensions.swift +++ /dev/null @@ -1,276 +0,0 @@ -// -// Extensions.swift -// Franz -// -// Created by Kellan Cummings on 1/14/16. -// Copyright © 2016 Kellan Cummings. All rights reserved. -// - -import Foundation - - -protocol VariableLengthDatable { - var data: Data { get } - init() - static func fromBytes( _ bytes: [UInt8]) -> VariableLengthDatable -} - - -protocol FixedLengthDatable { - init(_:Int) - func toInt() -> Int - var data: Data { get } - init(bytes: [UInt8]) -} - -extension Int8: FixedLengthDatable { - - init(bytes: [UInt8]) { - let data = Data(bytes: bytes, count: 1) - self.init(bigEndian: data.withUnsafeBytes { $0.pointee }) - } - - func toInt() -> Int { - return Int(self) - } - - var data: Data { - var bytes = self - return Data(bytes: &bytes, count: MemoryLayout.size) - } -} - -extension UInt8: FixedLengthDatable { - - init(bytes: [UInt8]) { - let data = Data(bytes: bytes, count: 1) - self.init(bigEndian: data.withUnsafeBytes { $0.pointee }) - } - - func toInt() -> Int { - return Int(self) - } - - var data: Data { - var bytes = self - return Data(bytes: &bytes, count: MemoryLayout.size) - } -} - -extension Int16: FixedLengthDatable { - - init( bytes: [UInt8]) { - let data = Data(bytes: bytes, count: 2) - self.init(bigEndian: data.withUnsafeBytes { $0.pointee }) - } - - func toInt() -> Int { - return Int(self) - } - - var data: Data { - var bytes = self.bigEndian - return Data(bytes: &bytes, count: MemoryLayout.size) - } -} - -extension UInt16: FixedLengthDatable { - - init(bytes: [UInt8]) { - let data = Data(bytes: bytes, count: 2) - self.init(bigEndian: data.withUnsafeBytes { $0.pointee }) - } - - func toInt() -> Int { - return Int(self) - } - - var data: Data { - var bytes = self.bigEndian - return Data(bytes: &bytes, count: MemoryLayout.size) - } -} - -extension Int32: FixedLengthDatable { - - init(bytes: [UInt8]) { - let data = Data(bytes: bytes, count: 4) - self.init(bigEndian: data.withUnsafeBytes { $0.pointee }) - } - - func toInt() -> Int { - return Int(self) - } - - var data: Data { - var bytes = self.bigEndian - return Data(bytes: &bytes, count: MemoryLayout.size) - } -} - -extension UInt32: FixedLengthDatable { - - init(bytes: [UInt8]) { - let data = Data(bytes: bytes, count: 4) - self.init(bigEndian: data.withUnsafeBytes { $0.pointee }) - } - - func toInt() -> Int { - return Int(self) - } - - var data: Data { - var bytes = self.bigEndian - return Data(bytes: &bytes, count: MemoryLayout.size) - } -} - -extension Int: FixedLengthDatable { - - init(bytes: [UInt8]) { - let data = Data(bytes: bytes, count: 4) - self.init(bigEndian: data.withUnsafeBytes { $0.pointee }) - } - - func toInt() -> Int { - return self - } - - init(value: FixedLengthDatable) { - var dataBytes = value.data - let data = NSData(bytes: &dataBytes, length: MemoryLayout.size) - - var out: Int = 0 - data.getBytes(&out, length: MemoryLayout.size) - - self.init(bigEndian: out.bigEndian) - } - - var data: Data { - var bytes = self.bigEndian - return Data(bytes: &bytes, count: MemoryLayout.size) - } -} - -extension Int64: FixedLengthDatable { - - init( bytes: [UInt8]) { - let data = Data(bytes: bytes, count: 8) - self.init(bigEndian: data.withUnsafeBytes { $0.pointee }) - } - - func toInt() -> Int { - return Int(self) - } - - var data: Data { - var bytes = self.bigEndian - return Data(bytes: &bytes, count: MemoryLayout.size) - } -} - -extension UInt64: FixedLengthDatable { - - init(bytes: [UInt8]) { - let data = Data(bytes: bytes, count: 8) - self.init(bigEndian: data.withUnsafeBytes { $0.pointee }) - } - - func toInt() -> Int { - return Int(self) - } - - var data: Data { - var bytes = self.bigEndian - return Data(bytes: &bytes, count: MemoryLayout.size) - } -} - -extension String: VariableLengthDatable { - - static func fromBytes(_ bytes: [UInt8]) -> VariableLengthDatable { - var bytes = bytes - let data = Data(buffer: UnsafeBufferPointer(start: &bytes, count: bytes.count)) - let string = String(data: data, encoding: String.Encoding.utf8) ?? "" - return self.init(string) - } - - var data: Data { - return self.data( - using: String.Encoding.utf8, - allowLossyConversion: true - ) ?? Data() - } -} - -extension Data: VariableLengthDatable { - - static func fromBytes(_ bytes: [UInt8]) -> VariableLengthDatable { - return Data(bytes: bytes) - } - - var data: Data { - return self - } -} - - -extension Stream.Event { - var description: String { - switch self { - case []: - return "None" - case .openCompleted: - return "Open Completed" - case .hasBytesAvailable: - return "Has Bytes Available" - case .hasSpaceAvailable: - return "Has Space Available" - case .errorOccurred: - return "Error Occurred" - case .endEncountered: - return "End Encountered" - default: - return "" - } - } -} - -extension Stream.Status { - var description: String { - switch self { - case .notOpen: - return "Not Open" - case .opening: - return "Opening" - case .open: - return "Open" - case .reading: - return "Reading" - case .writing: - return "Writing" - case .atEnd: - return "End" - case .closed: - return "Closed" - case .error: - return "Error" - } - } -} - -extension Array { - - mutating func slice(_ offset: Int, length: Int) -> [Element] { - var values = [Element]() - if self.count >= offset + length { - for _ in offset..<(offset + length) { - let value = self.remove(at: offset) - values.append(value) - } - } - - return values - } - -} diff --git a/Sources/Franz/FetchAPI.swift b/Sources/Franz/FetchAPI.swift index 6a51e22..f309eb1 100644 --- a/Sources/Franz/FetchAPI.swift +++ b/Sources/Franz/FetchAPI.swift @@ -78,13 +78,13 @@ class FetchRequest: KafkaRequest { } convenience init( - partitions: [String:[Int32:Int64]], + topics: [TopicName: [PartitionId: Offset]], replicaId: ReplicaId = .none, minBytes: MinBytes = .one, maxWaitTime: Int32 = 500 ) { let message = FetchRequestMessage( - partitions: partitions, + partitions: topics, replicaId: replicaId, minBytes: minBytes, maxWaitTime: maxWaitTime @@ -99,23 +99,27 @@ class FetchRequest: KafkaRequest { } -class FetchRequestMessage: KafkaClass { +class FetchRequestMessage: KafkaType { - private var _replicaId: KafkaInt32 - private var _maxWaitTime: KafkaInt32 - private var _minBytes: KafkaInt32 + private var _replicaId: Int32 + private var _maxWaitTime: Int32 + private var _minBytes: Int32 private var _topics: KafkaArray var replicaId: Int32 { - return _replicaId.value + return _replicaId } var minBytes: Int32 { - return _minBytes.value + return _minBytes } + + var topics: [TopicalFetchMessage] { + return _topics.values + } init( - partitions: [String:[Int32:Int64]], + partitions: [TopicName: [PartitionId: Offset]], replicaId: ReplicaId = .debug, minBytes: MinBytes = .one, maxWaitTime: Int32 = 500 @@ -126,28 +130,28 @@ class FetchRequestMessage: KafkaClass { tempTopics.append(TopicalFetchMessage(value: topic, partitions: ps)) } - _topics = KafkaArray(values: tempTopics) - _replicaId = KafkaInt32(value: replicaId.value) - _minBytes = KafkaInt32(value: minBytes.value) - _maxWaitTime = KafkaInt32(value: Int32(maxWaitTime)) + _topics = KafkaArray(tempTopics) + _replicaId = replicaId.value + _minBytes = minBytes.value + _maxWaitTime = Int32(maxWaitTime) } - required init( bytes: inout [UInt8]) { - _replicaId = KafkaInt32(bytes: &bytes) - _maxWaitTime = KafkaInt32(bytes: &bytes) - _minBytes = KafkaInt32(bytes: &bytes) - _topics = KafkaArray(bytes: &bytes) + required init(data: inout Data) { + _replicaId = Int32(data: &data) + _maxWaitTime = Int32(data: &data) + _minBytes = Int32(data: &data) + _topics = KafkaArray(data: &data) } - lazy var length: Int = { - return self._replicaId.length + - self._maxWaitTime.length + - self._minBytes.length + - self._topics.length + lazy var dataLength: Int = { + return self._replicaId.dataLength + + self._maxWaitTime.dataLength + + self._minBytes.dataLength + + self._topics.dataLength }() lazy var data: Data = { - var data = Data(capacity: self.length) + var data = Data(capacity: self.dataLength) data.append(self._replicaId.data) data.append(self._maxWaitTime.data) data.append(self._minBytes.data) @@ -156,30 +160,25 @@ class FetchRequestMessage: KafkaClass { //print(self.description) return data }() - - lazy var description: String = { - return "FETCH REQUEST(\(self.length)):\n" + - "\tREPLICA ID(\(self._replicaId.length)): \(self.replicaId) => \(self._replicaId.data)\n" + - "\tMAX WAIT TIME(\(self._maxWaitTime.length)): \(self._maxWaitTime.value) => \(self._maxWaitTime.data)\n" + - "\tMIN BYTES(\(self._minBytes.length)): \(self._minBytes.value) => \(self._minBytes.data)\n" + - "\tTOPICS(\(self._topics.length)):" + - self._topics.description - }() } -class TopicalFetchMessage: KafkaClass { - private var _topicName: KafkaString +class TopicalFetchMessage: KafkaType { + private var _topicName: String private var _partitions: KafkaArray - var topicName: String { - return _topicName.value ?? String() + var topicName: TopicName { + return _topicName } + + var partitions: [PartitionedFetchMessage] { + return _partitions.values + } init( value: String, - partitions: [Int32: Int64] + partitions: [PartitionId: Offset] ) { - _topicName = KafkaString(value: value) + _topicName = value var tempPartitions = [PartitionedFetchMessage]() for (partition, offset) in partitions { //print("PARTITION(\(partition)), OFFSET(\(offset))") @@ -187,184 +186,167 @@ class TopicalFetchMessage: KafkaClass { PartitionedFetchMessage(value: partition, offset: offset) ) } - _partitions = KafkaArray(values: tempPartitions) + _partitions = KafkaArray(tempPartitions) } - required init( bytes: inout [UInt8]) { - _topicName = KafkaString(bytes: &bytes) - _partitions = KafkaArray(bytes: &bytes) + required init(data: inout Data) { + _topicName = String(data: &data) + _partitions = KafkaArray(data: &data) } - lazy var length: Int = { - return self._topicName.length + self._partitions.length + lazy var dataLength: Int = { + return self._topicName.dataLength + self._partitions.dataLength }() lazy var data: Data = { - var data = Data(capacity: self.length) + var data = Data(capacity: self.dataLength) data.append(self._topicName.data) data.append(self._partitions.data) return data }() - - lazy var description: String = { - return "\t\tTOPIC NAME(\(self._topicName.length)): " + - "\(self.topicName) => \(self._topicName.data)\n" + - "\t\tPARTITIONS(\(self._partitions.length)):" + - self._partitions.description - }() } -class PartitionedFetchMessage: KafkaClass { - private var _partition: KafkaInt32 - private var _fetchOffset: KafkaInt64 - private var _maxBytes: KafkaInt32 = KafkaInt32(value: 6400) +class PartitionedFetchMessage: KafkaType { + private var _partition: Int32 + private var _fetchOffset: Int64 + private var _maxBytes: Int32 = 6400 - var partition: Int32 { - return _partition.value + var partition: PartitionId { + return _partition } - var offset: Int64 { - return _fetchOffset.value + var offset: Offset { + return _fetchOffset } var maxBytes: Int32 { - return _maxBytes.value + return _maxBytes } init(value: Int32, offset: Int64 = 0) { - _partition = KafkaInt32(value: value) - _fetchOffset = KafkaInt64(value: offset) + _partition = value + _fetchOffset = offset } - required init( bytes: inout [UInt8]) { - _partition = KafkaInt32(bytes: &bytes) - _fetchOffset = KafkaInt64(bytes: &bytes) - _maxBytes = KafkaInt32(bytes: &bytes) + required init(data: inout Data) { + _partition = Int32(data: &data) + _fetchOffset = Int64(data: &data) + _maxBytes = Int32(data: &data) } - lazy var length: Int = { - return self._partition.length + - self._fetchOffset.length + - self._maxBytes.length + lazy var dataLength: Int = { + return self._partition.dataLength + + self._fetchOffset.dataLength + + self._maxBytes.dataLength }() lazy var data: Data = { - var data = Data(capacity: self.length) + var data = Data(capacity: self.dataLength) data.append(self._partition.data) data.append(self._fetchOffset.data) data.append(self._maxBytes.data) return data }() - - lazy var description: String = { - return "\n\t\t\t----------\n" + - "\t\t\tPARTITION(\(self._partition.length)): \(self.partition) => \(self._partition.data)\n" + - "\t\t\tFETCH OFFSET(\(self._fetchOffset.length)): \(self.offset) => \(self._fetchOffset.data)\n" + - "\t\t\tMAX BYTES(\(self._maxBytes.length)): \(self.maxBytes) => \(self._maxBytes.data)" - - }() + } class FetchResponse: KafkaResponse { + + var data: Data { + return _topics.data + } + + var dataLength: Int { + return _topics.dataLength + } + private var _topics: KafkaArray - required init( bytes: inout [UInt8]) { - _topics = KafkaArray(bytes: &bytes) - super.init(bytes: &bytes) + required init(data: inout Data) { + _topics = KafkaArray(data: &data) } - - override var description: String { - return _topics.description - } - + var topics: [TopicalFetchResponse] { return _topics.values } } -class TopicalFetchResponse: KafkaClass { - private var _topicName: KafkaString +class TopicalFetchResponse: KafkaType { + private var _topicName: String private var _partitions: KafkaArray - var topicName: String { - return _topicName.value ?? String() + var topicName: TopicName { + return _topicName } var partitions: [PartitionedFetchResponse] { return _partitions.values } - required init( bytes: inout [UInt8]) { - _topicName = KafkaString(bytes: &bytes) - _partitions = KafkaArray(bytes: &bytes) + required init(data: inout Data) { + _topicName = String(data: &data) + _partitions = KafkaArray(data: &data) } - lazy var length: Int = { - return self._topicName.length + self._partitions.length + lazy var dataLength: Int = { + return self._topicName.dataLength + self._partitions.dataLength }() lazy var data: Data = { - var data = Data(capacity: self.length) + var data = Data(capacity: self.dataLength) data.append(self._topicName.data) data.append(self._partitions.data) return data }() - - lazy var description: String = { - return "\t\tTOPIC NAME(\(self._topicName.length )): " + - "\(self.topicName) => \(self._topicName.data)\n" + - "\t\tPARTITIONS(\(self._partitions.length)):" + - self._partitions.description - }() } -class PartitionedFetchResponse: KafkaClass { +class PartitionedFetchResponse: KafkaType { - private var _partition: KafkaInt32 - private var _errorCode: KafkaInt16 - private var _highwaterMarkOffset: KafkaInt64 - private var _messageSetSize: KafkaInt32 + private var _partition: Int32 + private var _errorCode: Int16 + private var _highwaterMarkOffset: Int64 + private var _messageSetSize: Int32 private var _messageSet: MessageSet - var partition: Int32 { - return _partition.value + var partition: PartitionId { + return _partition } var error: KafkaErrorCode? { - return KafkaErrorCode(rawValue: _errorCode.value) + return KafkaErrorCode(rawValue: _errorCode) } var offset: Int64 { - return _highwaterMarkOffset.value + return _highwaterMarkOffset } var messages: [Message] { - return _messageSet.messages + return _messageSet.values.map { $0.message } } - - required init( bytes: inout [UInt8]) { - _partition = KafkaInt32(bytes: &bytes) - _errorCode = KafkaInt16(bytes: &bytes) - _highwaterMarkOffset = KafkaInt64(bytes: &bytes) - _messageSetSize = KafkaInt32(bytes: &bytes) - var messageSetBytes = bytes.slice(0, length: _messageSetSize.value.toInt()) - _messageSet = MessageSet(bytes: &messageSetBytes) + + required init(data: inout Data) { + _partition = Int32(data: &data) + _errorCode = Int16(data: &data) + _highwaterMarkOffset = Offset(data: &data) + _messageSetSize = Int32(data: &data) + var messageSetData = data.take(first: Int(_messageSetSize)) + _messageSet = MessageSet(data: &messageSetData) } - lazy var length: Int = { - return self._partition.length + - self._errorCode.length + - self._highwaterMarkOffset.length + - self._messageSetSize.length + - self._messageSet.length + lazy var dataLength: Int = { + return self._partition.dataLength + + self._errorCode.dataLength + + self._highwaterMarkOffset.dataLength + + self._messageSetSize.dataLength + + self._messageSet.dataLength }() lazy var data: Data = { - var data = Data(capacity: self.length) + var data = Data(capacity: self.dataLength) data.append(self._partition.data) data.append(self._errorCode.data) data.append(self._highwaterMarkOffset.data) @@ -372,15 +354,4 @@ class PartitionedFetchResponse: KafkaClass { data.append(self._messageSet.data) return data }() - - lazy var description: String = { - return "\n\t\t\t---------\n" + - "\t\t\tPARTITION: \(self.partition) => \(self._partition.data)\n" + - "\t\t\tERROR CODE: \(self.error?.code ?? 0)\n" + - "\t\t\tERROR DESCRIPTION: \(self.error?.description ?? String())\n" + - "\t\t\tHIGHWATER MARK OFFSET: \(self.offset) => \(self._highwaterMarkOffset.data)\n" + - "\t\t\tMESSAGE SET SIZE: \(self._messageSetSize.value) => \(self._messageSetSize.data)\n" + - "\t\t\tMESSAGE SET(\(self._messageSet.length)):" + - self._messageSet.description - }() } diff --git a/Sources/Franz/Group.swift b/Sources/Franz/Group.swift index a77869d..9fe53f0 100644 --- a/Sources/Franz/Group.swift +++ b/Sources/Franz/Group.swift @@ -67,6 +67,9 @@ open class Group { callback(id, state) } } + + var topics = Set() + internal(set) var assignedPartitions = [TopicName: [PartitionId]]() } @@ -97,6 +100,7 @@ open class ConsumerGroup: Group { open class GroupMembership { var _group: Group var _memberId: String + let members: [Member] /** A Group @@ -112,16 +116,17 @@ open class GroupMembership { return _memberId } - init(group: Group, memberId: String) { + init(group: Group, memberId: String, members: [Member]) { self._group = group self._memberId = memberId + self.members = members } /** Sync the broker with the Group Coordinator */ - open func sync( - _ topics: [String: [Int32]], + func sync( + _ topics: [TopicName: [PartitionId]], data: Data = Data(), callback: (() -> ())? = nil ) { @@ -132,9 +137,13 @@ open class GroupMembership { topics: topics, userData: data, clientId: group._clientId, - version: group._version, - callback: callback - ) + version: group._version) { membership in + for assignment in membership.partitionAssignment.values { + self.group.assignedPartitions[assignment.topic] = assignment.partitions.values.map { $0 } + } + self.group.topics = Set(membership.partitionAssignment.values.map { $0.topic }) + callback?() + } } /** diff --git a/Sources/Franz/GroupCoordinatorAPI.swift b/Sources/Franz/GroupCoordinatorAPI.swift index 4555f0e..a0bf9eb 100644 --- a/Sources/Franz/GroupCoordinatorAPI.swift +++ b/Sources/Franz/GroupCoordinatorAPI.swift @@ -21,90 +21,85 @@ class GroupCoordinatorRequest: KafkaRequest { } -class GroupCoordinatorRequestMessage: KafkaClass { +class GroupCoordinatorRequestMessage: KafkaType { - private var _groupId: KafkaString + private var _groupId: String init(groupId: String) { - _groupId = KafkaString(value: groupId) + _groupId = groupId } - required init( bytes: inout [UInt8]) { - _groupId = KafkaString(bytes: &bytes) + required init(data: inout Data) { + _groupId = String(data: &data) } - lazy var length: Int = { - return self._groupId.length + lazy var dataLength: Int = { + return self._groupId.dataLength }() lazy var data: Data = { - var data = Data(capacity: self.length) + var data = Data(capacity: self.dataLength) data.append(self._groupId.data) return data }() var id: String { - return _groupId.value ?? String() + return _groupId } - - lazy var description: String = { - return "GROUP ID(\(self._groupId.length)): \(self.id) => \(self._groupId.data)\n" - }() } class GroupCoordinatorResponse: KafkaResponse { - var _errorCode: KafkaInt16 - var _coordinatorId: KafkaInt32 - var _coordinatorHost: KafkaString - var _coordinatorPort: KafkaInt32 + var _errorCode: Int16 + var _coordinatorId: Int32 + var _coordinatorHost: String + var _coordinatorPort: Int32 var error: KafkaErrorCode? { - return KafkaErrorCode(rawValue: _errorCode.value) + return KafkaErrorCode(rawValue: _errorCode) } var id: Int32 { - return _coordinatorId.value + return _coordinatorId } var host: String { - return _coordinatorHost.value ?? String() + return _coordinatorHost } var port: Int32 { - return _coordinatorPort.value + return _coordinatorPort } - - required init( bytes: inout [UInt8]) { - _errorCode = KafkaInt16(bytes: &bytes) - _coordinatorId = KafkaInt32(bytes: &bytes) - _coordinatorHost = KafkaString(bytes: &bytes) - _coordinatorPort = KafkaInt32(bytes: &bytes) - super.init(bytes: &bytes) + + //TODO: Convert to struct + init(errorCode: Int16, coordinatorId: Int32, coordinatorHost: String, coordinatorPort: Int32) { + _errorCode = errorCode + _coordinatorId = coordinatorId + _coordinatorHost = coordinatorHost + _coordinatorPort = coordinatorPort + } + + required init(data: inout Data) { + _errorCode = Int16(data: &data) + _coordinatorId = Int32(data: &data) + _coordinatorHost = String(data: &data) + _coordinatorPort = Int32(data: &data) } - lazy var length: Int = { - return self._errorCode.length + - self._coordinatorId.length + - self._coordinatorHost.length + - self._coordinatorPort.length + lazy var dataLength: Int = { + return self._errorCode.dataLength + + self._coordinatorId.dataLength + + self._coordinatorHost.dataLength + + self._coordinatorPort.dataLength }() lazy var data: Data = { - var data = Data(capacity: self.length) + var data = Data(capacity: self.dataLength) data.append(self._errorCode.data) data.append(self._coordinatorId.data) data.append(self._coordinatorHost.data) data.append(self._coordinatorPort.data) return data }() - - override var description: String { - return "ERROR CODE: \(self.error?.code ?? 0)\n" + - "ERROR DESCRIPTION: \(self.error?.description ?? String())\n" + - "COORDINATOR ID(\(self._coordinatorId.length)): \(id) => \(self._coordinatorId.data)\n" + - "COORDINATOR HOST(\(self._coordinatorHost.length)): \(host) => \(self._coordinatorHost.data)\n" + - "COORDINATOR PORT(\(self._coordinatorPort.length)): \(port) => \(self._coordinatorPort.data)\n" - } } diff --git a/Sources/Franz/GroupMembershipAPI.swift b/Sources/Franz/GroupMembershipAPI.swift index 18663e0..72ae2bc 100644 --- a/Sources/Franz/GroupMembershipAPI.swift +++ b/Sources/Franz/GroupMembershipAPI.swift @@ -34,12 +34,12 @@ class GroupMembershipRequest: KafkaRequest { } -class JoinGroupRequestMessage: KafkaClass { +class JoinGroupRequestMessage: KafkaType { - private var _groupId: KafkaString - private var _sessionTimeout: KafkaInt32 - private var _memberId: KafkaString - private var _protocolType: KafkaString + private var _groupId: String + private var _sessionTimeout: Int32 + private var _memberId: String + private var _protocolType: String private var _groupProtocols: KafkaArray> init( @@ -49,37 +49,37 @@ class JoinGroupRequestMessage: KafkaClass { protocolType: GroupProtocol, groupProtocols: [AssignmentStrategy: T] ) { - _groupId = KafkaString(value: groupId) - _sessionTimeout = KafkaInt32(value: sessionTimeout) - _memberId = KafkaString(value: memberId) - _protocolType = KafkaString(value: protocolType.value) + _groupId = groupId + _sessionTimeout = sessionTimeout + _memberId = memberId + _protocolType = protocolType.value var values = [JoinGroupProtocol]() for (name, metadata) in groupProtocols { values.append(JoinGroupProtocol(name: name.rawValue, metadata: metadata)) } - _groupProtocols = KafkaArray(values: values) + _groupProtocols = KafkaArray(values) } - required init( bytes: inout [UInt8]) { - _groupId = KafkaString(bytes: &bytes) - _sessionTimeout = KafkaInt32(bytes: &bytes) - _memberId = KafkaString(bytes: &bytes) - _protocolType = KafkaString(bytes: &bytes) - _groupProtocols = KafkaArray(bytes: &bytes) + required init(data: inout Data) { + _groupId = String(data: &data) + _sessionTimeout = Int32(data: &data) + _memberId = String(data: &data) + _protocolType = String(data: &data) + _groupProtocols = KafkaArray(data: &data) } - lazy var length: Int = { - return self._groupId.length + - self._sessionTimeout.length + - self._memberId.length + - self._protocolType.length + - self._groupProtocols.length + lazy var dataLength: Int = { + return self._groupId.dataLength + + self._sessionTimeout.dataLength + + self._memberId.dataLength + + self._protocolType.dataLength + + self._groupProtocols.dataLength }() lazy var data: Data = { - var data = Data(capacity: self.length) + var data = Data(capacity: self.dataLength) data.append(self._groupId.data) data.append(self._sessionTimeout.data) data.append(self._memberId.data) @@ -89,66 +89,47 @@ class JoinGroupRequestMessage: KafkaClass { }() var groupId: String { - return _groupId.value ?? String() + return _groupId } var protocolType: String { - return _protocolType.value ?? String() - } - - lazy var description: String = { - return """ - GROUP ID(\(self._groupId.length)): \(self.groupId) => \(self._groupId.data) - SESSION TIMEPOUT(\(self._sessionTimeout.length)): \(self._sessionTimeout.value) => \(self._sessionTimeout.data) - MEMBER ID(\(self._memberId.length)): \(self._memberId.value ?? "nil") => \(self._memberId.data) - PROTOCOL TYPE(\(self._protocolType.length)): \(self.protocolType) => \(self._protocolType.data) - GROUP PROTOCOLS(\(self._groupProtocols.length)): - \(self._groupProtocols.description) - """ - }() + return _protocolType + } } -class JoinGroupProtocol: KafkaClass { - private var _protocolName: KafkaString +class JoinGroupProtocol: KafkaType { + private var _protocolName: String private var _protocolMetadata: T init(name: String, metadata: T) { - _protocolName = KafkaString(value: name) + _protocolName = name _protocolMetadata = metadata } - required init( bytes: inout [UInt8]) { - _protocolName = KafkaString(bytes: &bytes) - _protocolMetadata = T(bytes: &bytes) + required init(data: inout Data) { + _protocolName = String(data: &data) + _protocolMetadata = T(data: &data) } - lazy var length: Int = { - return self._protocolName.length + - self._protocolMetadata.length + lazy var dataLength: Int = { + return self._protocolName.dataLength + + self._protocolMetadata.dataLength }() lazy var data: Data = { - var data = Data(capacity: self.length) + var data = Data(capacity: self.dataLength) data.append(self._protocolName.data) data.append(self._protocolMetadata.data) return data }() - - lazy var description: String = { - return """ - PROTOCOL NAME(\(self._protocolName.length)): \(self._protocolName.value ?? "nil") => \(self._protocolName.data) - PROTOCOL METADATA(\(self._protocolMetadata.length)): - \(self._protocolMetadata.description) - """ - }() } class ConsumerGroupMetadata: KafkaMetadata { - private var _version: KafkaInt16 - private var _subscription: KafkaArray - private var _userData: KafkaBytes + private var _version: Int16 + private var _subscription: KafkaArray + private var _userData: Data? static var protocolType: GroupProtocol { return GroupProtocol.consumer @@ -159,23 +140,23 @@ class ConsumerGroupMetadata: KafkaMetadata { userData: Data? = nil, version: ApiVersion = ApiVersion.defaultVersion ) { - _version = KafkaInt16(value: version.rawValue) - var values = [KafkaString]() + _version = version.rawValue + var values = [String]() for s in subscription { - values.append(KafkaString(value: s)) + values.append(s) } - _subscription = KafkaArray(values: values) - _userData = KafkaBytes(value: userData) + _subscription = KafkaArray(values) + _userData = userData } - required init(bytes: inout [UInt8]) { - _version = KafkaInt16(bytes: &bytes) - _subscription = KafkaArray(bytes: &bytes) - _userData = KafkaBytes(bytes: &bytes) + required init(data: inout Data) { + _version = Int16(data: &data) + _subscription = KafkaArray(data: &data) + _userData = Data(data: &data) } - lazy var length: Int = { + lazy var dataLength: Int = { return self.sizeDataLength + self.valueDataLength }() @@ -184,9 +165,9 @@ class ConsumerGroupMetadata: KafkaMetadata { }() lazy var valueDataLength: Int = { - return self._version.length + - self._subscription.length + - self._userData.length + return self._version.dataLength + + self._subscription.dataLength + + self._userData.dataLength }() private lazy var sizeData: Data = { @@ -194,55 +175,46 @@ class ConsumerGroupMetadata: KafkaMetadata { }() lazy var data: Data = { - var data = Data(capacity: self.length) + var data = Data(capacity: self.dataLength) data.append(self.sizeData) data.append(self._version.data) data.append(self._subscription.data) data.append(self._userData.data) return data }() - - lazy var description: String = { - return "\t\t\tSIZE(4): \(self.sizeData) => \(self.length)\n" + - "\t\t\tVERSION(\(self._version.length)): \(self._version.value) => \(self._version.data)\n" + - "\t\t\tSUBSCRIPTION(\(self._subscription.length)):\n" + - "\t\t\t\t\(self._subscription.description)\n" + - "\t\t\tUSER DATA(\(self._userData.length)):\n" + - "\t\t\t\t\(self._userData.description)" - }() } class JoinGroupResponse: KafkaResponse { - private var _errorCode: KafkaInt16 - private var _generationId: KafkaInt32 - private var _groupProtocol: KafkaString - private var _leaderId: KafkaString - private var _memberId: KafkaString + private var _errorCode: Int16 + private var _generationId: Int32 + private var _groupProtocol: String + private var _leaderId: String + private var _memberId: String private var _members: KafkaArray var error: KafkaErrorCode? { - return KafkaErrorCode(rawValue: _errorCode.value) + return KafkaErrorCode(rawValue: _errorCode) } var generationId: Int32 { - return _generationId.value + return _generationId } var memberId: String { - return _memberId.value ?? String() + return _memberId } var leaderId: String { - return _leaderId.value ?? String() + return _leaderId } var groupProtocol: GroupProtocol { - if _groupProtocol.value == GroupProtocol.consumer.value { + if _groupProtocol == GroupProtocol.consumer.value { return GroupProtocol.consumer } else { - return GroupProtocol.custom(name: _groupProtocol.value ?? String()) + return GroupProtocol.custom(name: _groupProtocol) } } @@ -250,63 +222,58 @@ class JoinGroupResponse: KafkaResponse { return _members.values } - required init(bytes: inout [UInt8]) { - _errorCode = KafkaInt16(bytes: &bytes) - _generationId = KafkaInt32(bytes: &bytes) - _groupProtocol = KafkaString(bytes: &bytes) - _leaderId = KafkaString(bytes: &bytes) - _memberId = KafkaString(bytes: &bytes) - _members = KafkaArray(bytes: &bytes) - super.init(bytes: &bytes) - } - - override var description: String { - let error = self.error?.description ?? String() - - return "JOIN GROUP RESPONSE:\n" + - "\tERROR CODE(\(_errorCode.length)): \(error) => \(_errorCode.data)\n" + - "\tGENERATION ID(\(_generationId.length)): \(generationId) => \(_generationId.data)\n" + - "\tGROUP PROTOCOL(\(_groupProtocol.length)): \(groupProtocol) => \(_groupProtocol.data)\n" + - "\tLEADER ID(\(_groupProtocol.length)): \(leaderId) => \(_leaderId.data)\n" + - "\tMEMBER ID(\(_memberId.length)): \(memberId) => \(_memberId.data)\n" + - "\tMEMBERS(\(_members.length)):\n" + - _members.description - } + required init(data: inout Data) { + _errorCode = Int16(data: &data) + _generationId = Int32(data: &data) + _groupProtocol = String(data: &data) + _leaderId = String(data: &data) + _memberId = String(data: &data) + _members = KafkaArray(data: &data) + } + + var data: Data { + let values: [KafkaType] = [_errorCode, _generationId, _groupProtocol, _leaderId, _memberId, _members] + return values.map { $0.data }.reduce(Data(), +) + } + + var dataLength: Int { + let values: [KafkaType] = [_errorCode, _generationId, _groupProtocol, _leaderId, _memberId, _members] + return values.map { $0.dataLength }.reduce(0, +) + } } -class Member: KafkaClass { - private var _memberName: KafkaString - private var _memberMetadata: KafkaBytes +class Member: KafkaType { + private var _memberName: String + private var _memberMetadata: Data + + var memberId: MemberId { + return _memberName + } var name: String { - return _memberName.value ?? String() + return _memberName } init(name: String, metadata: String) { - _memberName = KafkaString(value: name) - _memberMetadata = KafkaBytes(value: metadata) + _memberName = name + _memberMetadata = metadata.data(using: .utf8)! } - required init(bytes: inout [UInt8]) { - _memberName = KafkaString(bytes: &bytes) - _memberMetadata = KafkaBytes(bytes: &bytes) + required init(data: inout Data) { + _memberName = String(data: &data) + _memberMetadata = Data(data: &data) } - lazy var length: Int = { - return self._memberName.length + self._memberMetadata.length + lazy var dataLength: Int = { + return self._memberName.dataLength + self._memberMetadata.dataLength }() lazy var data: Data = { - var data = Data(capacity: self.length) + var data = Data(capacity: self.dataLength) data.append(self._memberName.data) data.append(self._memberMetadata.data) return data }() - - lazy var description: String = { - return "\t\tNAME(\(self._memberName.length)): \(self.name) => \(self._memberName.data)\n" + - "\t\tMETADATA(\(self._memberMetadata.length)): \(self._memberMetadata) => \(self._memberMetadata.data)" - }() } @@ -316,7 +283,7 @@ class SyncGroupRequest: KafkaRequest { groupId: String, generationId: Int32, memberId: String, - groupAssignment: [T] + groupAssignment: [String: T] ) { let request = SyncGroupRequestMessage( groupId: groupId, @@ -329,21 +296,21 @@ class SyncGroupRequest: KafkaRequest { } init(value: SyncGroupRequestMessage) { - super.init(apiKey: ApiKey.syncGroupRequest, value: value) + super.init(apiKey: .syncGroupRequest, value: value) } } class GroupMemberAssignment: KafkaMetadata { - private var _version: KafkaInt16 - private var _partitionAssignment: KafkaArray - private var _userData: KafkaBytes + private var _version: Int16 + let partitionAssignment: KafkaArray + private var _userData: Data static var protocolType: GroupProtocol { return GroupProtocol.consumer } - init(topics: [String: [Int32]], userData: Data, version: ApiVersion) { - _version = KafkaInt16(value: version.rawValue) + init(topics: [TopicName: [PartitionId]], userData: Data, version: ApiVersion) { + _version = version.rawValue var values = [PartitionAssignment]() for (topic, partitions) in topics { @@ -351,112 +318,104 @@ class GroupMemberAssignment: KafkaMetadata { PartitionAssignment(topic: topic, partitions: partitions) ) } - _partitionAssignment = KafkaArray(values: values) - _userData = KafkaBytes(value: userData) + partitionAssignment = KafkaArray(values) + _userData = userData } - required init(bytes: inout [UInt8]) { - _version = KafkaInt16(bytes: &bytes) - _partitionAssignment = KafkaArray(bytes: &bytes) - _userData = KafkaBytes(bytes: &bytes) + required init(data: inout Data) { + if data.count <= 0 { + _version = 0 + partitionAssignment = KafkaArray([]) + _userData = Data() + return + } + _version = Int16(data: &data) + partitionAssignment = KafkaArray(data: &data) + _userData = Data(data: &data) } - lazy var length: Int = { - return self._version.length + - self._partitionAssignment.length + - self._userData.length + lazy var dataLength: Int = { + return self._version.dataLength + + self.partitionAssignment.dataLength + + self._userData.dataLength }() lazy var data: Data = { - var data = Data(capacity: self.length) + var data = Data(capacity: self.dataLength) data.append(self._version.data) - data.append(self._partitionAssignment.data) + data.append(self.partitionAssignment.data) data.append(self._userData.data) return data }() - - lazy var description: String = { - return """ - VERSION(\(self._version.length)): \(self._version.data) => \(self._version.value) - PARTITION ASSIGNMENT(\(self._partitionAssignment.length)): - \(self._partitionAssignment.description) - USER DATA\(self._userData.length): \(self._userData.data) => \(String(describing: self._userData.value)) - """ - }() } -class PartitionAssignment: KafkaClass { - private var _topic: KafkaString - private var _partitions: KafkaArray +class PartitionAssignment: KafkaType { + let topic: String + let partitions: KafkaArray init(topic: String, partitions: [Int32]) { - _topic = KafkaString(value: topic) - var values = [KafkaInt32]() + self.topic = topic + var values = [Int32]() for partition in partitions { - values.append(KafkaInt32(value: partition)) + values.append(partition) } - _partitions = KafkaArray(values: values) + self.partitions = KafkaArray(values) } - required init(bytes: inout [UInt8]) { - _topic = KafkaString(bytes: &bytes) - _partitions = KafkaArray(bytes: &bytes) + required init(data: inout Data) { + topic = String(data: &data) + partitions = KafkaArray(data: &data) } - lazy var length: Int = { - return self._topic.length + self._partitions.length + lazy var dataLength: Int = { + return self.topic.dataLength + self.partitions.dataLength }() lazy var data: Data = { - var data = Data(capacity: self.length) - data.append(self._topic.data) - data.append(self._partitions.data) + var data = Data(capacity: self.dataLength) + data.append(self.topic.data) + data.append(self.partitions.data) return data }() - - lazy var description: String = { - return """ - TOPIC(\(self._topic.length)): \(self._topic.value ?? "nil") - PARTITIONS(\(self._partitions.length)): \(self._partitions.values) - """ - }() } -class SyncGroupRequestMessage: KafkaClass { +typealias MemberId = String + +class SyncGroupRequestMessage: KafkaType { - private var _groupId: KafkaString - private var _generationId: KafkaInt32 - private var _memberId: KafkaString - private var _groupAssignment: KafkaArray + private var _groupId: String + private var _generationId: Int32 + private var _memberId: String + private var _groupAssignment: KafkaArray> init( groupId: String, generationId: Int32, memberId: String, - groupAssignment: [T] + groupAssignment: [String: T] ) { - _groupId = KafkaString(value: groupId) - _generationId = KafkaInt32(value: generationId) - _memberId = KafkaString(value: memberId) - _groupAssignment = KafkaArray(values: groupAssignment) + _groupId = groupId + _generationId = generationId + _memberId = memberId + _groupAssignment = KafkaArray(groupAssignment.map { GroupAssignment(memberId: $0, memberAssignment: $1) }) } - required init(bytes: inout [UInt8]) { - _groupId = KafkaString(bytes: &bytes) - _generationId = KafkaInt32(bytes: &bytes) - _memberId = KafkaString(bytes: &bytes) - _groupAssignment = KafkaArray(bytes: &bytes) + required init(data: inout Data) { + _groupId = String(data: &data) + _generationId = Int32(data: &data) + _memberId = String(data: &data) + _groupAssignment = KafkaArray(data: &data) } - lazy var length: Int = { - return self._groupId.length + - self._generationId.length + - self._memberId.length + - self._groupAssignment.length + lazy var dataLength: Int = { + return self._groupId.dataLength + + self._generationId.dataLength + + self._memberId.dataLength + + self._groupAssignment.dataLength }() lazy var data: Data = { - var data = Data(capacity: self.length) + var data = Data(capacity: self.dataLength) data.append(self._groupId.data) data.append(self._generationId.data) data.append(self._memberId.data) @@ -465,28 +424,55 @@ class SyncGroupRequestMessage: KafkaClass { }() var groupId: String { - return _groupId.value ?? String() - } - - lazy var description: String = { - return "\tGROUP ID(\(self._groupId.length)): " - }() + return _groupId + } + + + + class GroupAssignment: KafkaType { + + let memberId: String + let memberAssignment: T + + private var memberAssignmentData: Data { + return Data(memberAssignment.data) + } + + required init(data: inout Data) { + memberId = String(data: &data) + memberAssignment = T(data: &data) + } + + init(memberId: String, memberAssignment: T) { + self.memberId = memberId + self.memberAssignment = memberAssignment + } + + var data: Data { + return memberId.data + memberAssignmentData.data + } + + var dataLength: Int { + return memberId.dataLength + memberAssignmentData.dataLength + } + + } } class SyncGroupResponse: KafkaResponse { - private var _errorCode: KafkaInt16 - private var _memberAssignment: T + private var _errorCode: Int16 + let memberAssignment: T - required init(bytes: inout [UInt8]) { - _errorCode = KafkaInt16(bytes: &bytes) - _memberAssignment = T(bytes: &bytes) - super.init(bytes: &bytes) + required init(data: inout Data) { + _errorCode = Int16(data: &data) + var memberAssignmentData = Data(data: &data) + memberAssignment = T(data: &memberAssignmentData) } - lazy var length: Int = { - return self._errorCode.length + self._memberAssignment.length + lazy var dataLength: Int = { + return self._errorCode.dataLength + memberAssignment.dataLength + 4 }() lazy var data: Data = { @@ -494,15 +480,9 @@ class SyncGroupResponse: KafkaResponse { }() lazy var error: KafkaErrorCode? = { - return KafkaErrorCode(rawValue: self._errorCode.value) + return KafkaErrorCode(rawValue: self._errorCode) }() - - override var description: String { - return "SYNC GROUP RESPONSE(\(self.length))\n" + - "\tERROR(\(self._errorCode.length)): \(self.error?.description ?? String())\n" + - "\tMEMBER ASSIGNMENT(\(self._memberAssignment.length)):\n" + - self._memberAssignment.description - } + } @@ -528,32 +508,32 @@ class HeartbeatRequest: KafkaRequest { } -class HeartbeatRequestMessage: KafkaClass { +class HeartbeatRequestMessage: KafkaType { - private var _groupId: KafkaString - private var _generationId: KafkaInt32 - private var _memberId: KafkaString + private var _groupId: String + private var _generationId: Int32 + private var _memberId: String init(groupId: String, generationId: Int32, memberId: String) { - _groupId = KafkaString(value: groupId) - _generationId = KafkaInt32(value: generationId) - _memberId = KafkaString(value: memberId) + _groupId = groupId + _generationId = generationId + _memberId = memberId } - required init(bytes: inout [UInt8]) { - _groupId = KafkaString(bytes: &bytes) - _generationId = KafkaInt32(bytes: &bytes) - _memberId = KafkaString(bytes: &bytes) + required init(data: inout Data) { + _groupId = String(data: &data) + _generationId = Int32(data: &data) + _memberId = String(data: &data) } - lazy var length: Int = { - return self._groupId.length + - self._generationId.length + - self._memberId.length + lazy var dataLength: Int = { + return self._groupId.dataLength + + self._generationId.dataLength + + self._memberId.dataLength }() lazy var data: Data = { - var data = Data(capacity: self.length) + var data = Data(capacity: self.dataLength) data.append(self._groupId.data) data.append(self._generationId.data) data.append(self._memberId.data) @@ -561,26 +541,22 @@ class HeartbeatRequestMessage: KafkaClass { }() var groupId: String { - return _groupId.value ?? String() + return _groupId } - - lazy var description: String = { - return "\tGROUP ID(\(self._groupId.length)): " - }() + } class HeartbeatResponse: KafkaResponse { - private var _errorCode: KafkaInt16 + private var _errorCode: Int16 - required init(bytes: inout [UInt8]) { - _errorCode = KafkaInt16(bytes: &bytes) - super.init(bytes: &bytes) + required init(data: inout Data) { + _errorCode = Int16(data: &data) } - lazy var length: Int = { - return self._errorCode.length + lazy var dataLength: Int = { + return self._errorCode.dataLength }() lazy var data: Data = { @@ -588,12 +564,9 @@ class HeartbeatResponse: KafkaResponse { }() lazy var error: KafkaErrorCode? = { - return KafkaErrorCode(rawValue: self._errorCode.value) + return KafkaErrorCode(rawValue: self._errorCode) }() - - override var description: String { - return "\tERROR(\(self._errorCode.length)): \(self.error?.description ?? String())" - } + } @@ -617,53 +590,48 @@ class LeaveGroupRequest: KafkaRequest { } -class LeaveGroupRequestMessage: KafkaClass { - private var _groupId: KafkaString - private var _memberId: KafkaString +class LeaveGroupRequestMessage: KafkaType { + private var _groupId: String + private var _memberId: String init(groupId: String, memberId: String) { - _groupId = KafkaString(value: groupId) - _memberId = KafkaString(value: memberId) + _groupId = groupId + _memberId = memberId } - required init(bytes: inout [UInt8]) { - _groupId = KafkaString(bytes: &bytes) - _memberId = KafkaString(bytes: &bytes) + required init(data: inout Data) { + _groupId = String(data: &data) + _memberId = String(data: &data) } - lazy var length: Int = { - return self._groupId.length + - self._memberId.length + lazy var dataLength: Int = { + return self._groupId.dataLength + + self._memberId.dataLength }() lazy var data: Data = { - var data = Data(capacity: self.length) + var data = Data(capacity: self.dataLength) data.append(self._groupId.data) data.append(self._memberId.data) return data }() var groupId: String { - return _groupId.value ?? String() + return _groupId } - - lazy var description: String = { - return "\tGROUP ID(\(self._groupId.length)): " - }() } class LeaveGroupResponse: KafkaResponse { - private var _errorCode: KafkaInt16 + private var _errorCode: Int16 - required init(bytes: inout [UInt8]) { - _errorCode = KafkaInt16(bytes: &bytes) - super.init(bytes: &bytes) + required init(data: inout Data) { + _errorCode = Int16(data: &data) } - lazy var length: Int = { - return self._errorCode.length + lazy var dataLength: Int = { + return self._errorCode.dataLength }() lazy var data: Data = { @@ -671,10 +639,7 @@ class LeaveGroupResponse: KafkaResponse { }() lazy var error: KafkaErrorCode? = { - return KafkaErrorCode(rawValue: self._errorCode.value) + return KafkaErrorCode(rawValue: self._errorCode) }() - - override var description: String { - return "\tERROR(\(self._errorCode.length)): \(self.error?.description ?? String())" - } } + diff --git a/Sources/Franz/KafkaProtocol.swift b/Sources/Franz/KafkaProtocol.swift index 0227239..47bd7f9 100644 --- a/Sources/Franz/KafkaProtocol.swift +++ b/Sources/Franz/KafkaProtocol.swift @@ -8,290 +8,228 @@ import Foundation - -protocol Readable { - init(bytes: inout [UInt8]) -} - - -protocol KafkaType: Readable { - var data: Data { get } - var length: Int { get } - var description: String { get } -} - - -class KafkaFixedLengthType: KafkaType { - var value: T - - required init(value: T) { - self.value = value - } - - required init(bytes: inout [UInt8]) { - let slice = bytes.slice(0, length: MemoryLayout.size) - self.value = T(bytes: slice) - } - - var length: Int { - return MemoryLayout.size - } - - lazy var description: String = { - return "(\(self.length)): \(self.value) => \(self.data)" - }() - - lazy var data: Data = { - return (self.value.data) - }() -} - - -class KafkaVariableLengthType: KafkaType { - var value: T? - - required init(value: T?) { - self.value = value - } - - required init(bytes: inout [UInt8]) { - let sizeSlice = bytes.slice(0, length: MemoryLayout.size) - let size = E(bytes: sizeSlice).toInt() - - if size > 0 { - let slice = bytes.slice(0, length: size) - if let value = T.fromBytes(slice) as? T { - self.value = value - } else{ - self.value = T() - } - } else { - self.value = T() - } - } - - lazy var length: Int = { - if let value = self.value { - return self.valueDataLength + self.sizeDataLength - } else { - return self.sizeDataLength - } - }() - - lazy var valueData: Data = { - return self.value?.data ?? E(-1).data - }() - - lazy var valueDataLength: Int = { - return self.valueData.count - }() - - lazy var description: String = { - return "(\(self.length)): \(String(describing: self.value)) => \(self.data)" - }() - - lazy var data: Data = { - var finalData = Data(capacity: self.length) - if let value = self.value { - finalData.append(E(self.valueData.count).data) - finalData.append(self.valueData) - } else { - finalData.append(self.valueData) - } - return finalData - }() - - let sizeDataLength = MemoryLayout.size -} - - -class KafkaArray: KafkaType, Readable { - - var values: [T] - - required init(values: [T]) { - self.values = values - } - - required init(bytes: inout [UInt8]) { - let sizeBytes = bytes.slice(0, length: 4) - let count = Int32(bytes: sizeBytes) - - values = [T]() - if count >= 0 { - for _ in 0.. { - - required init(value: Int8) { - super.init(value: value) - } - - required init(bytes: inout [UInt8]) { - super.init(bytes: &bytes) - } - +extension Data { + mutating func take(first: Int) -> Data { + let start = prefix(upTo: startIndex + first) + removeFirst(first) + return start + } } - -class KafkaInt16: KafkaFixedLengthType { - - required init(value: Int16) { - super.init(value: value) - } - - required init(bytes: inout [UInt8]) { - super.init(bytes: &bytes) - } - +extension KafkaType where Self: FixedWidthInteger { + init(data: inout Data) { + let dataLength = MemoryLayout.size + self.init(bigEndian: data.take(first: dataLength).withUnsafeBytes { $0.pointee }) + } + + var dataLength: Int { + return MemoryLayout.size + } + + var data: Data { + var bytes = Self(self.bigEndian) + return Data(bytes: &bytes, count: MemoryLayout.size) + } } - -class KafkaInt32: KafkaFixedLengthType { - - required init(value: Int32) { - super.init(value: value) - } - - required init(bytes: inout [UInt8]) { - super.init(bytes: &bytes) - } - +extension UInt: KafkaType {} +extension UInt8: KafkaType {} +extension UInt16: KafkaType {} +extension UInt32: KafkaType {} +extension UInt64: KafkaType {} +extension Int: KafkaType {} +extension Int8: KafkaType {} +extension Int16: KafkaType {} +extension Int32: KafkaType {} +extension Int64: KafkaType {} + +protocol KafkaVariableLengthType: KafkaType { + associatedtype Length: FixedWidthInteger where Length: KafkaType + + var variableLengthData: Data { get } + init(variableLengthData: Data) } - -class KafkaUInt32: KafkaFixedLengthType { - - required init(value: UInt32) { - super.init(value: value) - } - - required init(bytes: inout [UInt8]) { - super.init(bytes: &bytes) - } - +extension KafkaVariableLengthType { + + init(data: inout Data) { + let length = Int(Length(data: &data)) + if length == -1 { + self.init(variableLengthData: Data()) + return + } + self.init(variableLengthData: data.take(first: length)) + } + + var lengthSize: Int { + return MemoryLayout.size + } + + var dataLength: Int { + return variableLengthData.count + lengthSize + } + + var data: Data { + var finalData = Data(capacity: self.dataLength) + finalData += Length(self.variableLengthData.count).data + finalData += variableLengthData + return finalData + } + } - -class KafkaInt64: KafkaFixedLengthType { - - required init(value: Int64) { - super.init(value: value) - } - - required init(bytes: inout [UInt8]) { - super.init(bytes: &bytes) - } - +extension String: KafkaVariableLengthType { + typealias Length = Int16 + + init(variableLengthData: Data) { + self.init(data: variableLengthData, encoding: .utf8)! + } + + var variableLengthData: Data { + return data(using: .utf8)! + } } -class KafkaBytes: KafkaVariableLengthType, Hashable { - - var hashValue: Int { - return value?.hashValue ?? -1 - } - - convenience init(value: String?) { - self.init(value: value?.data) - } - - required init(value: Data?) { - super.init(value: value) - } - - required init(bytes: inout [UInt8]) { - super.init(bytes: &bytes) - } +extension Optional where Wrapped == String { + var dataLength: Int { + switch self { + case .none: + return "".dataLength + case .some(let wrapped): + return wrapped.dataLength + } + } + var data: Data { + switch self { + case .none: + return "".data + case .some(let wrapped): + return wrapped.data + } + } } - -class KafkaString: KafkaVariableLengthType, Hashable { - - var hashValue: Int { - return value?.hashValue ?? -1 - } - - required init(value: String?) { - super.init(value: value) - } - - required init(bytes: inout [UInt8]) { - super.init(bytes: &bytes) - } - +extension Data: KafkaVariableLengthType { + typealias Length = Int32 + + init(variableLengthData: Data) { + self = variableLengthData + } + + var variableLengthData: Data { + return self + } } - -func ==(lhs: KafkaString, rhs: KafkaString) -> Bool { - return lhs.value == rhs.value +extension Optional where Wrapped == Data { + var dataLength: Int { + switch self { + case .none: + return Data().dataLength + case .some(let wrapped): + return wrapped.dataLength + } + } + + var data: Data { + switch self { + case .none: + return Data().data + case .some(let wrapped): + return wrapped.data + } + } } -func ==(lhs: KafkaBytes, rhs: KafkaBytes) -> Bool { - return lhs.value == rhs.value +//TODO: Replace with Array extension conditional conformance is released +struct KafkaArray: KafkaType, Collection { + + typealias Element = T + typealias SubSequence = ArraySlice + typealias Index = Int + + var startIndex: Index { + return values.startIndex + } + + var endIndex: Index { + return values.endIndex + } + + subscript (position: Index) -> Iterator.Element { + return values[position] + } + + func index(after i: Index) -> Index { + return values.index(after: i) + } + + mutating func append(_ item: T) { + values.append(item) + } + + var values: [T] + + init(_ values: [T] = []) { + self.values = values + } + + init(data: inout Data) { + let count = Int32(data: &data) + + values = [T]() + + for _ in 0.. \(clientId.data) - \(value)) - """ - } } + diff --git a/Sources/Franz/KafkaResponse.swift b/Sources/Franz/KafkaResponse.swift index aa77279..e1fb486 100644 --- a/Sources/Franz/KafkaResponse.swift +++ b/Sources/Franz/KafkaResponse.swift @@ -8,10 +8,8 @@ import Foundation -class KafkaResponse: NSObject, Readable { - - required init(bytes: inout [UInt8]) { - - } - +protocol KafkaResponse { + + init(data: inout Data) + } diff --git a/Sources/Franz/MessageSet.swift b/Sources/Franz/MessageSet.swift index f5dd455..c811ff6 100644 --- a/Sources/Franz/MessageSet.swift +++ b/Sources/Franz/MessageSet.swift @@ -9,80 +9,40 @@ import Foundation -class MessageSet: KafkaClass { - private var _values: [MessageSetItem] - - var messages: [Message] { - var messages = [Message]() - - for value in _values { - messages.append(value.message) - } - - return messages - } - +class MessageSet: KafkaType { + let values: KafkaArray + init(values: [MessageSetItem]) { - self._values = values + self.values = KafkaArray(values) } - required init(bytes: inout [UInt8]) { - _values = [MessageSetItem]() - - while bytes.count > 0 { - _values.append(MessageSetItem(bytes: &bytes)) + required init(data: inout Data) { + var tempValues = KafkaArray() + + while data.count > 0 { + tempValues.append(MessageSetItem(data: &data)) } + + values = tempValues } - lazy var length: Int = { - return self.valueLength + 4 - }() + var dataLength: Int { + return values.map { $0.dataLength }.reduce(0, +) + } - lazy var valueLength: Int = { - var totalLength = 0 - - for value in self._values { - totalLength += value.length - } - - return totalLength - }() - - lazy var valueLengthData: Data = { - return (Int32(self.valueLength).data) - }() - - lazy var data: Data = { - var data = Data(capacity: self.length) - - data.append(self.valueLengthData) - - for value in self._values { - print("Appending \(value)") - data.append(value.data) - } - - return data - }() - - var description: String { - var str = "" - - for value in _values { - str += value.description - } - return str + var data: Data { + return values.map { $0.data }.reduce(Data(), +) } } -class MessageSetItem: KafkaClass { - var _offset: KafkaInt64 +class MessageSetItem: KafkaType { + var _offset: Int64 var _value: KafkaMessage - var _size: KafkaInt32? + var _size: Int32? var offset: Int64 { - return _offset.value + return _offset } var message: Message { @@ -90,72 +50,52 @@ class MessageSetItem: KafkaClass { } init(value: String, key: String? = nil, offset: Int = 0) { - self._offset = KafkaInt64(value: Int64(offset)) + self._offset = Int64(offset) self._value = KafkaMessage(value: value, key: key) } init(data: Data, key: Data? = nil, offset: Int = 0) { - self._offset = KafkaInt64(value: Int64(offset)) + self._offset = Int64(offset) self._value = KafkaMessage(data: data, key: key) } - required init(bytes: inout [UInt8]) { - _offset = KafkaInt64(bytes: &bytes) - _size = KafkaInt32(bytes: &bytes) - _value = KafkaMessage(bytes: &bytes) + required init(data: inout Data) { + _offset = Int64(data: &data) + _size = Int32(data: &data) + _value = KafkaMessage(data: &data) } lazy var messageSizeData: Data = { - return (Int32(self._value.length).data) + return (Int32(self._value.dataLength).data) }() let messageSizeDataLength = 4 - lazy var length: Int = { - return self._offset.length + + lazy var dataLength: Int = { + return self._offset.dataLength + self.messageSizeDataLength + - self._value.length + - (self._size != nil ? self._size!.length : 0) + self._value.dataLength + + (self._size != nil ? self._size!.dataLength : 0) }() lazy var data: Data = { - var data = Data(capacity: self.length) + var data = Data(capacity: self.dataLength) data.append(self._offset.data) data.append(self.messageSizeData) data.append(self._value.data) return data }() - - var description: String { - return "\n\t\t\t\t\t---------\n" + - "\t\t\t\t\tLENGTH: (\(length))\n" + - "\t\t\t\t\tOFFSET: \(_offset.data)\n" + - "\t\t\t\t\tMESSAGE SIZE: \(messageSizeData)\n" + - "\t\t\t\t\tMESSAGE: \n\(_value.description)\n" - } } -class KafkaMessage: KafkaClass { - private var _key: KafkaBytes - private var _value: KafkaBytes - private var _magicByte: KafkaInt8 - private var _attributes: KafkaInt8 - private var _crc: KafkaUInt32! = nil - - /** - Message data - */ - var value: Data { - return _value.valueData - } - - /** - Message key - */ - var key: Data? { - return _key.valueData - } +class KafkaMessage: KafkaType { + ///Message key + let key: Data? + ///Message data + let value: Data + private var _magicByte: Int8 + private var _attributes: Int8 + private var _crc: UInt32! = nil /** Initialize a new message using raw bytes @@ -164,10 +104,10 @@ class KafkaMessage: KafkaClass { - Parameter key: an optional key String. Can be used for partition assignment. */ init(data: Data, key: Data? = nil) { - self._attributes = KafkaInt8(value: CompressionCodec.none.rawValue) - self._magicByte = KafkaInt8(value: 0) - self._key = KafkaBytes(value: key) - self._value = KafkaBytes(value: data) + self._attributes = Int8(CompressionCodec.none.rawValue) + self._magicByte = Int8(0) + self.key = key + self.value = data } /** @@ -177,10 +117,10 @@ class KafkaMessage: KafkaClass { - Parameter key: an optional key String. Can be used for partition assignment. */ init(value: String, key: String? = nil) { - self._attributes = KafkaInt8(value: CompressionCodec.none.rawValue) - self._magicByte = KafkaInt8(value: 0) - self._key = KafkaBytes(value: key) - self._value = KafkaBytes(value: value) + self._attributes = Int8(CompressionCodec.none.rawValue) + self._magicByte = Int8(0) + self.key = key?.data(using: .utf8) + self.value = value.data(using: .utf8)! } /** @@ -189,47 +129,36 @@ class KafkaMessage: KafkaClass { - Parameter value: String value - Parameter key: an optional key String. Can be used for partition assignment. */ - required init(bytes: inout [UInt8]) { - _crc = KafkaUInt32(bytes: &bytes) - _magicByte = KafkaInt8(bytes: &bytes) - _attributes = KafkaInt8(bytes: &bytes) - _key = KafkaBytes(bytes: &bytes) - _value = KafkaBytes(bytes: &bytes) + required init(data: inout Data) { + _crc = UInt32(data: &data) + _magicByte = Int8(data: &data) + _attributes = Int8(data: &data) + key = Data(data: &data) + value = Data(data: &data) } - lazy var length: Int = { + lazy var dataLength: Int = { return self.valueLength + 4 }() lazy var valueLength: Int = { - return self._magicByte.length + - self._attributes.length + - self._key.length + - self._value.length + return _magicByte.dataLength + _attributes.dataLength + key.dataLength + value.dataLength }() lazy var data: Data = { var valueData = Data(capacity: self.valueLength) valueData.append(self._magicByte.data) valueData.append(self._attributes.data) - valueData.append(self._key.data) - valueData.append(self._value.data) + valueData.append(key.data) + valueData.append(value.data) - self._crc = KafkaUInt32(value: CRC32(data: valueData).crc) + self._crc = CRC32(data: valueData).crc - var data = Data(capacity: self.length) + var data = Data(capacity: self.dataLength) data.append(self._crc.data) data.append(valueData) return data }() - - var description: String { - return "\t\t\t\t\t\tMAGIC_BYTE(\(self._crc.length)): \(self._crc.value) => \(self._crc.data)\n" + - "\t\t\t\t\t\tMAGIC_BYTE(\(self._magicByte.length)): \(self._magicByte.value) => \(self._magicByte.data)\n" + - "\t\t\t\t\t\tATTRIBUTES(\(self._attributes.length)): \(self._attributes.value) => \(self._attributes.data)\n" + - "\t\t\t\t\t\tKEY(\(self._key.length)): \(self._key.value?.description ?? "nil") => \(self._key.data)\n" + - "\t\t\t\t\t\tVALUE(\(self._value.length)): \(self._value.value?.description ?? "nil") => \(self._value.data)\n" - } } diff --git a/Sources/Franz/MetadataAPI.swift b/Sources/Franz/MetadataAPI.swift index 5cc00b0..5203d08 100644 --- a/Sources/Franz/MetadataAPI.swift +++ b/Sources/Franz/MetadataAPI.swift @@ -28,36 +28,31 @@ class TopicMetadataRequest: KafkaRequest { } -class TopicMetadataRequestMessage: KafkaClass { +class TopicMetadataRequestMessage: KafkaType { - var values: KafkaArray + var values: KafkaArray init(values: [String]) { - var strings = [KafkaString]() + var strings = [String]() for value in values { - strings.append(KafkaString(value: value)) + strings.append(value) } - self.values = KafkaArray(values: strings) + self.values = KafkaArray(strings) } - required init(bytes: inout [UInt8]) { - values = KafkaArray(bytes: &bytes) + required init(data: inout Data) { + values = KafkaArray(data: &data) } - lazy var length: Int = { - return self.values.length + lazy var dataLength: Int = { + return self.values.dataLength }() lazy var data: Data = { return (self.values.data) }() - - lazy var description: String = { - return "\tTOPICS(\(self.values.length)):\n\t\t" + - self.values.description - }() } class MetadataResponse: KafkaResponse { @@ -83,23 +78,16 @@ class MetadataResponse: KafkaResponse { return values } - override var description: String { - var description = "BROKERS:\n" - for (_, broker) in brokers { - description += "-----------\n\(broker.description)\n" - } - - description += "-----------\nMETADATA:\n" - for (_, topic) in topics { - description += "-----------\n\(topic.description)\n" - } - - return description - } - - required init(bytes: inout [UInt8]) { - _metadataBrokers = KafkaArray(bytes: &bytes) - _topicMetadata = KafkaArray(bytes: &bytes) - super.init(bytes: &bytes) + required init(data: inout Data) { + _metadataBrokers = KafkaArray(data: &data) + _topicMetadata = KafkaArray(data: &data) } + + var data: Data { + return _metadataBrokers.data + _topicMetadata.data + } + + var dataLength: Int { + return _metadataBrokers.dataLength + _topicMetadata.dataLength + } } diff --git a/Sources/Franz/OffsetAPI.swift b/Sources/Franz/OffsetAPI.swift index b9f3a8a..4833470 100644 --- a/Sources/Franz/OffsetAPI.swift +++ b/Sources/Franz/OffsetAPI.swift @@ -11,27 +11,29 @@ import Foundation class OffsetRequest: KafkaRequest { convenience init( - topic: String, - partitions: [Int32], + topics: [TopicName: [PartitionId]], time: TimeOffset = TimeOffset.latest, maxNumberOfOffsets: Int32 = 10, replicaId: ReplicaId = .none ) { - var topicValues = [Int32:(TimeOffset, Int32)]() - for partition in partitions { - topicValues[partition] = (time, maxNumberOfOffsets) - } + let topicsWithSettings = topics.mapValues { partitions -> [PartitionId: (TimeOffset, Int32)] in + var topicValues = [PartitionId: (TimeOffset, Int32)]() + for partition in partitions { + topicValues[partition] = (time, maxNumberOfOffsets) + } + return topicValues + } self.init( value: OffsetRequestMessage( - topics: [topic: topicValues], + topics: topicsWithSettings, replicaId: replicaId ) ) } convenience init( - topics: [String:[Int32:(TimeOffset,Int32)]], + topics: [TopicName: [PartitionId: (TimeOffset, Int32)]], replicaId: ReplicaId = .none ) { self.init( @@ -49,20 +51,20 @@ class OffsetRequest: KafkaRequest { } -class OffsetRequestMessage: KafkaClass { +class OffsetRequestMessage: KafkaType { - private var _replicaId: KafkaInt32 + private var _replicaId: Int32 private var _topics: KafkaArray var replicaId: Int32 { - return _replicaId.value + return _replicaId } init( topics: [String:[Int32:(TimeOffset,Int32)]], replicaId: ReplicaId = .none ) { - _replicaId = KafkaInt32(value: replicaId.value) + _replicaId = replicaId.value var tempTopics = [TopicalOffsetMessage]() @@ -70,48 +72,41 @@ class OffsetRequestMessage: KafkaClass { tempTopics.append(TopicalOffsetMessage(value: t, partitions: p)) } - _topics = KafkaArray(values: tempTopics) + _topics = KafkaArray(tempTopics) } - required init(bytes: inout [UInt8]) { - _replicaId = KafkaInt32(bytes: &bytes) - _topics = KafkaArray(bytes: &bytes) + required init(data: inout Data) { + _replicaId = Int32(data: &data) + _topics = KafkaArray(data: &data) } - lazy var length: Int = { - return self._replicaId.length + - self._topics.length + lazy var dataLength: Int = { + return self._replicaId.dataLength + + self._topics.dataLength }() lazy var data: Data = { - var data = Data(capacity: self.length) + var data = Data(capacity: self.dataLength) data.append(self._replicaId.data) data.append(self._topics.data) return data }() - - lazy var description: String = { - return "FETCH REQUEST(\(self.length)):\n" + - "\tREPLICA ID(\(self._replicaId.length)): \(self.replicaId) => \(self._replicaId.data)\n" + - "\tTOPICS(\(self._topics.length)):" + - self._topics.description - }() } -class TopicalOffsetMessage: KafkaClass { - private var _topicName: KafkaString +class TopicalOffsetMessage: KafkaType { + private var _topicName: String private var _partitions: KafkaArray var topicName: String { - return _topicName.value ?? String() + return _topicName } init( value: String, partitions: [Int32: (TimeOffset, Int32)] ) { - _topicName = KafkaString(value: value) + _topicName = value var tempPartitions = [PartitionedOffsetMessage]() for (partition, attributes) in partitions { tempPartitions.append( @@ -122,97 +117,78 @@ class TopicalOffsetMessage: KafkaClass { ) ) } - _partitions = KafkaArray(values: tempPartitions) + _partitions = KafkaArray(tempPartitions) } - required init(bytes: inout [UInt8]) { - _topicName = KafkaString(bytes: &bytes) - _partitions = KafkaArray(bytes: &bytes) + required init(data: inout Data) { + _topicName = String(data: &data) + _partitions = KafkaArray(data: &data) } - lazy var length: Int = { - return self._topicName.length + self._partitions.length + lazy var dataLength: Int = { + return self._topicName.dataLength + self._partitions.dataLength }() lazy var data: Data = { - var data = Data(capacity: self.length) + var data = Data(capacity: self.dataLength) data.append(self._topicName.data) data.append(self._partitions.data) return data }() - - lazy var description: String = { - return "\t\tTOPIC NAME(\(self._topicName.length)): " + - "\(self.topicName) => \(self._topicName.data)\n" + - "\t\tPARTITIONS(\(self._partitions.length)):" + - self._partitions.description - }() } -class PartitionedOffsetMessage: KafkaClass { - private var _partition: KafkaInt32 - private var _time: KafkaInt64 - private var _maxNumberOfOffsets: KafkaInt32 +class PartitionedOffsetMessage: KafkaType { + private var _partition: Int32 + private var _time: Int64 + private var _maxNumberOfOffsets: Int32 var partition: Int32 { - return _partition.value + return _partition } var time: Int64 { - return _time.value + return _time } var maxNumberOfOffsets: Int32 { - return _maxNumberOfOffsets.value + return _maxNumberOfOffsets } init(value: Int32, time: TimeOffset, maxNumberOfOffsets: Int32) { - _partition = KafkaInt32(value: Int32(value)) - _time = KafkaInt64(value: time.value) - _maxNumberOfOffsets = KafkaInt32(value: maxNumberOfOffsets) + _partition = value + _time = time.value + _maxNumberOfOffsets = maxNumberOfOffsets } - required init(bytes: inout [UInt8]) { - _partition = KafkaInt32(bytes: &bytes) - _time = KafkaInt64(bytes: &bytes) - _maxNumberOfOffsets = KafkaInt32(bytes: &bytes) + required init(data: inout Data) { + _partition = Int32(data: &data) + _time = Int64(data: &data) + _maxNumberOfOffsets = Int32(data: &data) } - lazy var length: Int = { - return self._partition.length + - self._time.length + - self._maxNumberOfOffsets.length + lazy var dataLength: Int = { + return self._partition.dataLength + + self._time.dataLength + + self._maxNumberOfOffsets.dataLength }() lazy var data: Data = { - var data = Data(capacity: self.length) + var data = Data(capacity: self.dataLength) data.append(self._partition.data) data.append(self._time.data) data.append(self._maxNumberOfOffsets.data) return data }() - - lazy var description: String = { - return "\n\t\t\t----------\n" + - "\t\t\tPARTITION(\(self._partition.length)): \(self.partition) => \(self._partition.data)\n" + - "\t\t\tFETCH OFFSET(\(self._time.length)): \(self.time) => \(self._time.data)\n" + - "\t\t\tMAX BYTES(\(self._maxNumberOfOffsets.length)): \(self._maxNumberOfOffsets.value) => \(self._maxNumberOfOffsets.data)" - - }() } class OffsetResponse: KafkaResponse { + var values: KafkaArray - required init(bytes: inout [UInt8]) { - values = KafkaArray(bytes: &bytes) - super.init(bytes: &bytes) - } - - override var description: String { - return values.description + required init(data: inout Data) { + values = KafkaArray(data: &data) } var topicalPartitionedOffsets: [TopicalPartitionedOffsets] { @@ -221,12 +197,12 @@ class OffsetResponse: KafkaResponse { } -class TopicalPartitionedOffsets: KafkaClass { - private var _topicName: KafkaString +class TopicalPartitionedOffsets: KafkaType { + private var _topicName: String private var _partitions: KafkaArray var topicName: String { - return _topicName.value ?? String() + return _topicName } var partitionedOffsets: [Int32: PartitionedOffsets] { @@ -237,76 +213,62 @@ class TopicalPartitionedOffsets: KafkaClass { return values } - required init(bytes: inout [UInt8]) { - _topicName = KafkaString(bytes: &bytes) - _partitions = KafkaArray(bytes: &bytes) + required init(data: inout Data) { + _topicName = String(data: &data) + _partitions = KafkaArray(data: &data) } - lazy var length: Int = { - return self._topicName.length + self._partitions.length + lazy var dataLength: Int = { + return self._topicName.dataLength + self._partitions.dataLength }() lazy var data: Data = { - var data = Data(capacity: self.length) + var data = Data(capacity: self.dataLength) data.append(self._topicName.data) data.append(self._partitions.data) return data }() - - lazy var description: String = { - return "\t\tTOPIC NAME(\(self._topicName.length )): \(self.topicName) => \(self._topicName.data)\n" + - "\t\tPARTITIONS(\(self._partitions.length)):" + - self._partitions.description - }() } -class PartitionedOffsets: KafkaClass { - private var _partition: KafkaInt32 - private var _errorCode: KafkaInt16 - private var _offsets: KafkaArray +class PartitionedOffsets: KafkaType { + private var _partition: Int32 + private var _errorCode: Int16 + private var _offsets: KafkaArray var partition: Int32 { - return _partition.value + return _partition } var error: KafkaErrorCode? { - return KafkaErrorCode(rawValue: _errorCode.value) + return KafkaErrorCode(rawValue: _errorCode) } var offsets: [Int64] { var values = [Int64]() for value in _offsets.values { - values.append(value.value) + values.append(value) } return values.reversed() } - required init(bytes: inout [UInt8]) { - _partition = KafkaInt32(bytes: &bytes) - _errorCode = KafkaInt16(bytes: &bytes) - _offsets = KafkaArray(bytes: &bytes) + required init(data: inout Data) { + _partition = Int32(data: &data) + _errorCode = Int16(data: &data) + _offsets = KafkaArray(data: &data) } - lazy var length: Int = { - return self._partition.length + - self._errorCode.length + - self._offsets.length + lazy var dataLength: Int = { + return self._partition.dataLength + + self._errorCode.dataLength + + self._offsets.dataLength }() lazy var data: Data = { - var data = Data(capacity: self.length) + var data = Data(capacity: self.dataLength) data.append(self._partition.data) data.append(self._errorCode.data) data.append(self._offsets.data) return data }() - - lazy var description: String = { - return "\n\t\t\t---------\n" + - "\t\t\tPARTITION(\(self._partition.length)): \(self.partition) => \(self._partition.data)\n" + - "\t\t\tERROR CODE(\(self._errorCode.length)): \(self.error?.description ?? String())(\(self.error?.code ?? 0)) => \(self._errorCode.data)\n" + - "\t\tOFFSETS(\(self._offsets.length)):" + - self._offsets.description - }() } diff --git a/Sources/Franz/OffsetCommitAPI.swift b/Sources/Franz/OffsetCommitAPI.swift index 15c3f1f..1c1fbc9 100644 --- a/Sources/Franz/OffsetCommitAPI.swift +++ b/Sources/Franz/OffsetCommitAPI.swift @@ -8,14 +8,15 @@ import Foundation +typealias OffsetMetadata = String class OffsetCommitRequest: KafkaRequest { - + convenience init( consumerGroupId: String, generationId: Int32, consumerId: String, - topics: [String:[Int32: (Int64, String?)]], + topics: [TopicName: [PartitionId: (Offset, OffsetMetadata?)]], retentionTime: Int64 = 0 ) { self.init( @@ -34,231 +35,168 @@ class OffsetCommitRequest: KafkaRequest { } } -class OffsetCommitRequestMessage: KafkaClass { +class OffsetCommitRequestMessage: KafkaType { - private var _consumerGroupId: KafkaString - private var _consumerGroupGenerationId: KafkaInt32 - private var _consumerId: KafkaString - private var _retentionTime: KafkaInt64 - private var _topics: KafkaArray + let consumerGroupId: String + let consumerGroupGenerationId: Int32 + let consumerId: String + let retentionTime: Int64 + let topics: KafkaArray - init( - consumerGroupId: String, - generationId: Int32, - consumerId: String, - topics: [String:[Int32: (Int64, String?)]], - retentionTime: Int64 = 0 - ) { - var values = [OffsetCommitTopic]() - for (key, value) in topics { - let offsetCommitTopic = OffsetCommitTopic(topic: key, partitions: value) - values.append(offsetCommitTopic) - } - _consumerGroupId = KafkaString(value: consumerGroupId) - _consumerGroupGenerationId = KafkaInt32(value: generationId) - _consumerId = KafkaString(value: consumerId) - _retentionTime = KafkaInt64(value: retentionTime) - _topics = KafkaArray(values: values) - } - - required init(bytes: inout [UInt8]) { - _consumerGroupId = KafkaString(bytes: &bytes) - _consumerGroupGenerationId = KafkaInt32(bytes: &bytes) - _consumerId = KafkaString(bytes: &bytes) - _retentionTime = KafkaInt64(bytes: &bytes) - _topics = KafkaArray(bytes: &bytes) - } - - lazy var length: Int = { - return self._consumerGroupId.length + - self._consumerGroupGenerationId.length + - self._consumerId.length + - self._retentionTime.length + - self._topics.length - }() - - lazy var data: Data = { - var data = Data(capacity: self.length) - data.append(self._consumerGroupId.data) - data.append(self._consumerGroupGenerationId.data) - data.append(self._consumerId.data) - data.append(self._retentionTime.data ) - data.append(self._topics.data) + init(consumerGroupId: String, generationId: Int32, consumerId: String, topics: [TopicName: [PartitionId: (Offset, OffsetMetadata?)]], retentionTime: Int64 = 0) { + self.consumerGroupId = consumerGroupId + self.consumerGroupGenerationId = generationId + self.consumerId = consumerId + self.retentionTime = retentionTime + self.topics = KafkaArray(topics.map { arg in + let (key, value) = arg + return OffsetCommitTopic(topic: key, partitions: value) + }) + } + + required init(data: inout Data) { + consumerGroupId = String(data: &data) + consumerGroupGenerationId = Int32(data: &data) + consumerId = String(data: &data) + retentionTime = Int64(data: &data) + topics = KafkaArray(data: &data) + } + + var dataLength: Int { + let values: [KafkaType] = [consumerGroupId, consumerGroupGenerationId, consumerId, retentionTime, topics] + return values.map { $0.dataLength }.reduce(0, +) + } + + var data: Data { + var data = Data(capacity: dataLength) + data += consumerGroupId.data + data += consumerGroupGenerationId.data + data += consumerId.data + data += retentionTime.data + data += topics.data return data - }() - - lazy var description: String = { - return "OFFSET COMMIT REQUEST:\n" + - "\tCONSUMER GROUP ID: \(self._consumerGroupId.value ?? "nil")\n" + - "\tCONSUMER GROUP GENERATION ID: \(self._consumerGroupGenerationId.value)\n" + - "\tCONSUMER ID ID: \(self._consumerId.value ?? "nil")\n" + - "\tRETENTION TIME ID: \(self._retentionTime.value)\n" + - "\tTOPICS:\n" + - self._topics.description - }() + } } -class OffsetCommitTopic: KafkaClass { - private var _topicName: KafkaString - private var _partitions: KafkaArray +class OffsetCommitTopic: KafkaType { + let topicName: TopicName + let partitions: KafkaArray - init(topic: String, partitions: [Int32:(Int64, String?)]) { - _topicName = KafkaString(value: topic) - var values = [OffsetCommitPartitionOffset]() - for (key, value) in partitions { - values.append(OffsetCommitPartitionOffset(partition: key, offset: value.0, metadata: value.1)) - } - _partitions = KafkaArray(values: values) + init(topic: TopicName, partitions: [PartitionId: (Offset, OffsetMetadata?)]) { + self.topicName = topic + self.partitions = KafkaArray(partitions.map { arg in + let (key, value) = arg + return OffsetCommitPartitionOffset(partition: key, offset: value.0, metadata: value.1) + }) } - required init(bytes: inout [UInt8]) { - _topicName = KafkaString(bytes: &bytes) - _partitions = KafkaArray(bytes: &bytes) + required init(data: inout Data) { + topicName = String(data: &data) + partitions = KafkaArray(data: &data) } - lazy var length: Int = { - return self._topicName.length + self._partitions.length - }() + var dataLength: Int { + return topicName.dataLength + partitions.dataLength + } - lazy var data: Data = { - var data = Data(capacity: self.length) - data.append(self._topicName.data) - data.append(self._partitions.data) + var data: Data { + var data = Data(capacity: self.dataLength) + data += topicName.data + data += partitions.data return data - }() - - lazy var description: String = { - return "" - }() + } } +class OffsetCommitPartitionOffset: KafkaType { + let partition: PartitionId + let offset: Offset + let metadata: String? -class OffsetCommitPartitionOffset: KafkaClass { - private var _partition: KafkaInt32 - private var _offset: KafkaInt64 - private var _metadata: KafkaString - - init(partition: Int32, offset: Int64, metadata: String?) { - _partition = KafkaInt32(value: partition) - _offset = KafkaInt64(value: offset) - _metadata = KafkaString(value: metadata) + init(partition: PartitionId, offset: Offset, metadata: String? = nil) { + self.partition = partition + self.offset = offset + self.metadata = metadata } - required init(bytes: inout [UInt8]) { - _partition = KafkaInt32(bytes: &bytes) - _offset = KafkaInt64(bytes: &bytes) - _metadata = KafkaString(bytes: &bytes) + required init(data: inout Data) { + partition = PartitionId(data: &data) + offset = Offset(data: &data) + metadata = String(data: &data) } - lazy var length: Int = { - return self._partition.length + - self._offset.length + - self._metadata.length - }() - - lazy var data: Data = { - var data = Data(capacity: self.length) - data.append(self._partition.data) - data.append(self._offset.data) - data.append(self._metadata.data) + var dataLength: Int { + return partition.dataLength + offset.dataLength + metadata.dataLength + } + + var data: Data { + var data = Data(capacity: self.dataLength) + data += partition.data + data += offset.data + data += metadata.data return data - }() - - lazy var description: String = { - return "" - }() + } } class OffsetCommitResponse: KafkaResponse { - private var _topics: KafkaArray - - required init(bytes: inout [UInt8]) { - _topics = KafkaArray(bytes: &bytes) - super.init(bytes: &bytes) - } + let topics: KafkaArray - var topics: [OffsetCommitTopicResponse] { - return _topics.values + required init(data: inout Data) { + topics = KafkaArray(data: &data) } - lazy var length: Int = { - return self._topics.length - }() - - lazy var data: Data = { - var data = Data(capacity: self.length) - return data - }() - - override var description: String { - return _topics.description - } + var dataLength: Int { + return topics.dataLength + } + + var data: Data { + return topics.data + } } -class OffsetCommitTopicResponse: KafkaClass { - - private var _topicName: KafkaString - private var _partitions: KafkaArray +class OffsetCommitTopicResponse: KafkaType { - required init(bytes: inout [UInt8]) { - _topicName = KafkaString(bytes: &bytes) - _partitions = KafkaArray(bytes: &bytes) - } + let topicName: String + let partitions: KafkaArray - var partitions: [OffsetCommitPartitionResponse] { - return _partitions.values + required init(data: inout Data) { + topicName = String(data: &data) + partitions = KafkaArray(data: &data) } - lazy var length: Int = { - return self._topicName.length + self._partitions.length - }() - - lazy var data: Data = { - var data = Data(capacity: self.length) - return data - }() - - lazy var description: String = { - return "\tTOPIC: \(self._topicName.value ?? "nil")" + - "\tPARTITIONS:\n" + - self._partitions.description - }() + var dataLength: Int { + return topicName.dataLength + partitions.dataLength + } + + var data: Data { + return topicName.data + partitions.data + } } -class OffsetCommitPartitionResponse: KafkaClass { +class OffsetCommitPartitionResponse: KafkaType { - private var _partition: KafkaInt32 - private var _errorCode: KafkaInt16 - - var partition: Int32 { - return _partition.value - } + let partition: Int32 + private var errorCode: Int16 var error: KafkaErrorCode? { - return KafkaErrorCode(rawValue: _errorCode.value) + return KafkaErrorCode(rawValue: errorCode) } - required init(bytes: inout [UInt8]) { - _partition = KafkaInt32(bytes: &bytes) - _errorCode = KafkaInt16(bytes: &bytes) + required init(data: inout Data) { + partition = Int32(data: &data) + errorCode = Int16(data: &data) } - lazy var length: Int = { - return self._partition.length + self._errorCode.length - }() - - lazy var data: Data = { - var data = Data(capacity: self.length) - return data - }() + var dataLength: Int { + return partition.dataLength + errorCode.dataLength + } - lazy var description: String = { - return "\t\tPARTITION: \(self.partition)\n" + - "\t\tERROR: \(self.error?.description ?? String())" - }() + var data: Data { + return partition.data + errorCode.data + } + } diff --git a/Sources/Franz/OffsetFetchAPI.swift b/Sources/Franz/OffsetFetchAPI.swift index 2af1c44..8db242a 100644 --- a/Sources/Franz/OffsetFetchAPI.swift +++ b/Sources/Franz/OffsetFetchAPI.swift @@ -10,7 +10,6 @@ import Foundation class OffsetFetchRequest: KafkaRequest { - convenience init( consumerGroupId: String, topics: [String: [Int32]] @@ -29,9 +28,9 @@ class OffsetFetchRequest: KafkaRequest { } -class OffsetFetchRequestMessage: KafkaClass { +class OffsetFetchRequestMessage: KafkaType { - private var _consumerGroup: KafkaString + private var _consumerGroup: String private var _topics: KafkaArray init( @@ -43,76 +42,63 @@ class OffsetFetchRequestMessage: KafkaClass { let offsetCommitTopic = OffsetFetchTopic(topic: key, partitions: value) values.append(offsetCommitTopic) } - _consumerGroup = KafkaString(value: consumerGroup) - _topics = KafkaArray(values: values) + _consumerGroup = consumerGroup + _topics = KafkaArray(values) } - required init(bytes: inout [UInt8]) { - _consumerGroup = KafkaString(bytes: &bytes) - _topics = KafkaArray(bytes: &bytes) + required init(data: inout Data) { + _consumerGroup = String(data: &data) + _topics = KafkaArray(data: &data) } - lazy var length: Int = { - return self._consumerGroup.length + - self._topics.length + lazy var dataLength: Int = { + return self._consumerGroup.dataLength + + self._topics.dataLength }() lazy var data: Data = { - var data = Data(capacity: self.length) + var data = Data(capacity: self.dataLength) data.append(self._consumerGroup.data) data.append(self._topics.data) return data }() - - lazy var description: String = { - return "OFFSET COMMIT REQUEST:\n" + - "\tCONSUMER GROUP: \(self._consumerGroup.value ?? "nil")" + - "\tTOPICS:\n" + - self._topics.description - }() } -class OffsetFetchTopic: KafkaClass { +class OffsetFetchTopic: KafkaType { - private var _topicName: KafkaString - private var _partitions: KafkaArray + private var _topicName: String + private var _partitions: KafkaArray init( topic: String, partitions: [Int32] ) { - _topicName = KafkaString(value: topic) - var values = [KafkaInt32]() + _topicName = topic + var values = [Int32]() for partition in partitions { - values.append(KafkaInt32(value: partition)) + values.append(partition) } - _partitions = KafkaArray(values: values) + _partitions = KafkaArray(values) } - required init(bytes: inout [UInt8]) { - _topicName = KafkaString(bytes: &bytes) - _partitions = KafkaArray(bytes: &bytes) + required init(data: inout Data) { + _topicName = String(data: &data) + _partitions = KafkaArray(data: &data) } - lazy var length: Int = { - return self._topicName.length + - self._partitions.length + lazy var dataLength: Int = { + return self._topicName.dataLength + + self._partitions.dataLength }() lazy var data: Data = { - var data = Data(capacity: self.length) + var data = Data(capacity: self.dataLength) data.append(self._topicName.data) data.append(self._partitions.data) return data }() - - lazy var description: String = { - return "\tTOPIC: \(self._topicName.value ?? "nil")" + - "\tPARTITIONS:\n" + - self._partitions.description - }() } @@ -120,122 +106,103 @@ class OffsetFetchResponse: KafkaResponse { private var _topics: KafkaArray - required init(bytes: inout [UInt8]) { - _topics = KafkaArray(bytes: &bytes) - super.init(bytes: &bytes) + required init(data: inout Data) { + _topics = KafkaArray(data: &data) } var topics: [OffsetFetchTopicResponse] { return _topics.values } - lazy var length: Int = { - return self._topics.length + lazy var dataLength: Int = { + return self._topics.dataLength }() lazy var data: Data = { - var data = Data(capacity: self.length) + var data = Data(capacity: self.dataLength) data.append(self._topics.data) return data }() - - override var description: String { - return "OFFSET FETCH RESPONSE:\n" + - "\tTOPICS:\n" + - self._topics.description - } } -class OffsetFetchTopicResponse: KafkaClass { - private var _topicName: KafkaString +class OffsetFetchTopicResponse: KafkaType { + private var _topicName: String private var _partitions: KafkaArray var topic: String { - return _topicName.value ?? String() + return _topicName } var partitions: [OffsetFetchPartitionOffset] { return _partitions.values } - required init(bytes: inout [UInt8]) { - _topicName = KafkaString(bytes: &bytes) - _partitions = KafkaArray(bytes: &bytes) + required init(data: inout Data) { + _topicName = String(data: &data) + _partitions = KafkaArray(data: &data) } - lazy var length: Int = { - return self._topicName.length + self._partitions.length + lazy var dataLength: Int = { + return self._topicName.dataLength + self._partitions.dataLength }() lazy var data: Data = { - var data = Data(capacity: self.length) + var data = Data(capacity: self.dataLength) data.append(self._topicName.data) data.append(self._partitions.data) return data }() - - lazy var description: String = { - return "\t\tNAME: \(self.topic)\n" + - "\t\tPARTITIONS:\n" + - self._partitions.description - }() } +typealias Offset = Int64 -class OffsetFetchPartitionOffset: KafkaClass { - private var _partition: KafkaInt32 - private var _offset: KafkaInt64 - private var _metadata: KafkaString - private var _errorCode: KafkaInt16 +class OffsetFetchPartitionOffset: KafkaType { + private var _partition: Int32 + private var _offset: Offset + private var _metadata: String + private var _errorCode: Int16 var error: KafkaErrorCode? { - if _offset.value == -1 { + if _offset == -1 { return KafkaErrorCode.noError } else { - return KafkaErrorCode(rawValue: _errorCode.value) + return KafkaErrorCode(rawValue: _errorCode) } } var partition: Int32 { - return _partition.value + return _partition } var metadata: String { - return _metadata.value ?? String() + return _metadata } - var offset: Int64 { - return _offset.value == -1 ? 0 : _offset.value + var offset: Offset { + return _offset == -1 ? 0 : _offset } - required init(bytes: inout [UInt8]) { - _partition = KafkaInt32(bytes: &bytes) - _offset = KafkaInt64(bytes: &bytes) - _metadata = KafkaString(bytes: &bytes) - _errorCode = KafkaInt16(bytes: &bytes) + required init(data: inout Data) { + _partition = Int32(data: &data) + _offset = Int64(data: &data) + _metadata = String(data: &data) + _errorCode = Int16(data: &data) } - lazy var length: Int = { - return self._partition.length + - self._offset.length + - self._metadata.length + - self._errorCode.length + lazy var dataLength: Int = { + return self._partition.dataLength + + self._offset.dataLength + + self._metadata.dataLength + + self._errorCode.dataLength }() lazy var data: Data = { - var data = Data(capacity: self.length) + var data = Data(capacity: self.dataLength) data.append(self._partition.data) data.append(self._offset.data) data.append(self._metadata.data) data.append(self._errorCode.data) return data }() - - lazy var description: String = { - return "\t\t\tPARTITION: \(self.partition)\n" + - "\t\t\tOFFSET: \(self.offset)\n" + - "\t\t\tMETADATA: \(self.metadata)\n" + - "\t\t\tERROR: \(self.error?.description ?? String())" - }() } diff --git a/Sources/Franz/OldConsumers.swift b/Sources/Franz/OldConsumers.swift new file mode 100644 index 0000000..e3dfeb8 --- /dev/null +++ b/Sources/Franz/OldConsumers.swift @@ -0,0 +1,265 @@ +// +// OldConsumers.swift +// Franz +// +// Created by Luke Lau on 07/08/2017. +// + +import Foundation + +/** +Base consumer delegate. Used by SimpleConsumer. +*/ +@objc public protocol ConsumerDelegate { + /** + Called when the consumer has consumed a new Message + + - Parameter message: the returned message + - Parameter offset: the message offset + */ + func consumerDidReturnMessage(_ message: Message, offset: Int64) + + /** + Called when a fetch request has failed and cannot be retried. + + - Parameter topic: the topic requested from the server + - Parameter partition: the partition requested from the server + - Parameter offset: the last message offset requested from server + - Parameter errorId: the error Id returned from the server + - Parameter errorDescription: a description of the error returned from the server + */ + @objc optional func fetchDidFail( + _ topic: String, + partition: Int32, + errorId: Int16, + errorDescription: String + ) + + /** + Called when a fetch has failed. Gives client the chance to retry before shutting down. + + - Parameter topic: the topic requested from the server + - Parameter partition: the partition requested from the server + - Parameter offset: the last message offset requested from server + - Parameter errorId: the error Id returned from the server + - Parameter errorDescription: a description of the error returned from the server + + - Returns: true if broker should attempt to retry request, false if not + */ + @objc optional func shouldRetryFailedFetch( + _ topic: String, + partition: Int32, + errorId: Int16, + errorDescription: String + ) -> Bool + + /** + Called when the Consumer is ready to starting issuing pull requests. + + - Parameter consumer: a Consumer + */ + func consumerIsReady(_ consumer: OldConsumer) + + /** + Called if a Leader is not found for a topic-partition + + - Parameter topic: the topic + - Parameter partition: the partition + */ + @objc optional func topicPartitionLeaderNotFound(_ topic: String, partition: Int32) +} + + +/** +High-level consumer delegate. Used by HighLevelConsumer. +*/ +@objc public protocol HighLevelConsumerDelegate: ConsumerDelegate { + /** + Called after messages have been pulled for server. + + - Parameter topic: the topic + - Parameter partition: the partition + - Parameter offset: the offset + + - Returns: true if offset should be committed, false if otherwise + */ + @objc optional func shouldCommitOffset(_ topic: String, partition: Int32, offset: Int64) -> Bool + + /** + Called after messages have been pulled for server. + + - Parameter topic: the topic + - Parameter partition: the partition + - Parameter offset: the offset + + - Returns: additional metadata to send with offset commit to server + */ + @objc optional func shouldAttachOffsetMetadata(_ topic: String, partition: Int32, offset: Int64) -> String? + + /** + Called after offset has been successfully committed + + - Parameter topic: the topic + - Parameter partition: the partition + - Parameter offset: the offset + */ + @objc optional func offsetDidCommit(_ topic: String, partition: Int32, offset: Int64) + + /** + Called if offset commit has failed and cannot be retried. + + - Parameter topic: the topic + - Parameter partition: the partition + - Parameter offset: the offset + - Parameter errorId: error code id + - Parameter errorDescription: description of the error + */ + @objc optional func offsetCommitDidFail(_ topic: String, partition: Int32, offset: Int64, errorId: Int16, errorDescription: String) + + /** + Called if offset commit has failed and commit is retriable + + - Parameter topic: the topic + - Parameter partition: the partition + - Parameter offset: the offset + - Parameter errorId: error code id + - Parameter errorDescription: description of the error + + - Returns: true if offset commit should be retried, false if otherwise + */ + @objc optional func shouldRetryFailedOffsetCommit(_ topic: String, partition: Int32, offset: Int64, errorId: Int16, errorDescription: String) -> Bool +} + + +/* +Base consumer class +*/ +public class OldConsumer: NSObject { + internal var broker: Broker? + + fileprivate var _topic: String + fileprivate var _partition: Int32 + fileprivate var _clientId: String + + internal init(topic: String, partition: Int32, clientId: String) { + self._topic = topic + self._partition = partition + self._clientId = clientId + } +} + + +/** +Class implementing a simple consumer model. +*/ +@available(*, deprecated, message: "Use Consumer instead") +public class SimpleConsumer: OldConsumer { + /** + the delegate + */ + open var delegate: ConsumerDelegate + + internal init( + topic: String, + partition: Int32, + clientId: String, + delegate: ConsumerDelegate + ) { + self.delegate = delegate + super.init(topic: topic, partition: partition, clientId: clientId) + } + + /** + Poll for messages + + Parameter offset: starting offset + */ + open func poll(_ offset: Int64) { + guard let coordinator = broker else { + return + } + + _ = coordinator.poll(topics: [_topic: [_partition: offset]], clientId: _clientId, replicaId: .none, callback: { topicName, partitionId, offset, messages in + messages.forEach { self.delegate.consumerDidReturnMessage($0, offset: offset) } + }, errorCallback: { error in + switch error { + case .fetchFailed, .noConnection, .noGroupMembershipForBroker: + print("Failed polling with a non-kafka error") + case .kafkaError(let errorCode): + if errorCode.retriable, self.delegate.shouldRetryFailedFetch?(self._topic, partition: self._partition, errorId: errorCode.code, errorDescription: errorCode.description) ?? true { + self.poll(offset) + } + self.delegate.fetchDidFail?(self._topic, partition: self._partition, errorId: errorCode.code, errorDescription: errorCode.description) + } + }) + } +} + + +/** +Class implementing a high-level consumer. Managed by a group coordinator. +The delegate is called after each fetch to +*/ +@available(*, deprecated, message: "Use Consumer instead") +public class HighLevelConsumer: OldConsumer { + + /** + The Delegate + */ + open var delegate: HighLevelConsumerDelegate + + internal var membership: GroupMembership? + + internal init( + topic: String, + partition: Int32, + clientId: String, + delegate: HighLevelConsumerDelegate + ) { + self.delegate = delegate + super.init(topic: topic, partition: partition, clientId: clientId) + } + + /** + Poll for messages + */ + open func poll() { + guard let groupId = membership?.group.id else { + print("Cannot poll without group membership id") + return + } + guard let coordinator = broker else { + print("Cannot poll without broker") + return + } + + _ = coordinator.poll(topics: [_topic: [_partition]], fromStart: true, groupId: groupId, clientId: _clientId, replicaId: .none, callback: { (topicName, partitionId, offset, messages) in + for (idx, message) in messages.enumerated() { + self.delegate.consumerDidReturnMessage( + message, + offset: offset + Int64(idx) + ) + } + + guard self.delegate.shouldCommitOffset != nil && self.delegate.shouldCommitOffset!(self._topic, partition: self._partition, offset: offset) else { + return + } + + let metadata = self.delegate.shouldAttachOffsetMetadata?(topicName, partition: partitionId, offset: offset) + + coordinator.commitGroupOffset(groupId: groupId, topics: [topicName: [partitionId: (offset, metadata)]], clientId: self._clientId, callback: { + self.delegate.offsetDidCommit?(topicName, partition: partitionId, offset: offset) + }) + }, errorCallback: { error in + switch error { + case .fetchFailed, .noConnection, .noGroupMembershipForBroker: + print("Failed polling with a non-kafka error") + case .kafkaError(let errorCode): + if errorCode.retriable, self.delegate.shouldRetryFailedFetch?(self._topic, partition: self._partition, errorId: errorCode.code, errorDescription: errorCode.description) ?? true { + self.poll() + } + self.delegate.fetchDidFail?(self._topic, partition: self._partition, errorId: errorCode.code, errorDescription: errorCode.description) + } + }) + } +} diff --git a/Sources/Franz/Partition.swift b/Sources/Franz/Partition.swift index 888ed18..a84ecfe 100644 --- a/Sources/Franz/Partition.swift +++ b/Sources/Franz/Partition.swift @@ -8,93 +8,45 @@ import Foundation -class Partition: KafkaClass { - private var _partitionErrorCode: KafkaInt16 - private var _partitionId: KafkaInt32 - private var _leader: KafkaInt32 - private var _replicas: KafkaArray - private var _isr: KafkaArray +typealias PartitionId = Int32 +class Partition: KafkaType { + + private var partitionErrorCode: Int16 var error: KafkaErrorCode? { - if let error = KafkaErrorCode(rawValue: _partitionErrorCode.value) { - return error - } else { - return nil - } + return KafkaErrorCode(rawValue: partitionErrorCode) } - var id: Int32 { - return _partitionId.value - } + var id: PartitionId - var leader: Int32 { - return _leader.value - } + var leader: PartitionId - var replicas: [Int32] { - var values = [Int32]() - for replica in _replicas.values { - values.append(replica.value) - } - return values - } - - var isr: [Int32] { - var values = [Int32]() - for isr in _isr.values { - values.append(isr.value) - } - return values - } + private(set) var replicas: KafkaArray - lazy var description: String = { - let defaultErrorValue = "nil" - return "PARTITION METADATA\n\t" + - "ERROR: \(self.error?.description ?? defaultErrorValue)\n\t" + - "ERROR CODE: \(self.error?.code ?? 0)\n\t" + - "PARTITION ID(\(self._partitionId.length)): \(self.id) => \(self._partitionId.data)\n\t" + - "LEADER(\(self._leader.length)): \(self.leader) => \(self._leader.data)\n\t" + - "REPLICAS(\(self._replicas.length)): \(self.replicas) => \(self._replicas.data)\n\t" + - "ISR(\(self._isr.length)): \(self.isr) => \(self._isr.data)" - }() + private(set) var isr: KafkaArray - init( - partitionErrorCode: Int, - partitionId: Int, - leader: Int, - replicas: [Int], - isr: [Int] - ) { - _partitionErrorCode = KafkaInt16(value: Int16(partitionErrorCode)) - _partitionId = KafkaInt32(value: Int32(partitionId)) - _leader = KafkaInt32(value: Int32(leader)) - - var tempReplicas = [KafkaInt32]() - for replica in replicas { - tempReplicas.append(KafkaInt32(value: Int32(replica))) - } - _replicas = KafkaArray(values: tempReplicas) - - var tempIsr = [KafkaInt32]() - for value in isr { - tempIsr.append(KafkaInt32(value: Int32(value))) - } - _isr = KafkaArray(values: tempIsr) + init(partitionErrorCode: Int, partitionId: Int, leader: Int, replicas: [Int], isr: [Int]) { + self.partitionErrorCode = Int16(partitionErrorCode) + self.id = Int32(partitionId) + self.leader = Int32(leader) + + self.replicas = KafkaArray(replicas.map { Int32($0) }) + self.isr = KafkaArray(isr.map { Int32($0) }) } - required init(bytes: inout [UInt8]) { - _partitionErrorCode = KafkaInt16(bytes: &bytes) - _partitionId = KafkaInt32(bytes: &bytes) - _leader = KafkaInt32(bytes: &bytes) - _replicas = KafkaArray(bytes: &bytes) - _isr = KafkaArray(bytes: &bytes) + required init(data: inout Data) { + partitionErrorCode = Int16(data: &data) + id = PartitionId(data: &data) + leader = PartitionId(data: &data) + replicas = KafkaArray(data: &data) + isr = KafkaArray(data: &data) } - var length: Int { - return _partitionErrorCode.length + _partitionId.length + _leader.length + _replicas.length + _isr.length + var dataLength: Int { + return partitionErrorCode.dataLength + id.dataLength + leader.dataLength + replicas.dataLength + isr.dataLength } var data: Data { - return Data() + return partitionErrorCode.data + id.data + leader.data + replicas.data + isr.data } } diff --git a/Sources/Franz/ProduceAPI.swift b/Sources/Franz/ProduceAPI.swift index f6cafb1..4401a70 100644 --- a/Sources/Franz/ProduceAPI.swift +++ b/Sources/Franz/ProduceAPI.swift @@ -41,33 +41,33 @@ class ProduceRequest: KafkaRequest { } } -class ProduceRequestMessage: KafkaClass { +class ProduceRequestMessage: KafkaType { var values: KafkaArray - var requestAcks: KafkaInt16 - var timeout: KafkaInt32 + var requestAcks: Int16 + var timeout: Int32 init( values: [KafkaTopicalMessageSet], timeout: Int32 = Int32(0x05DC) ) { - self.values = KafkaArray(values: values) - self.requestAcks = KafkaInt16(value: RequestAcknowledgement.noResponse.value) - self.timeout = KafkaInt32(value: timeout) + self.values = KafkaArray(values) + self.requestAcks = RequestAcknowledgement.noResponse.value + self.timeout = timeout } - required init(bytes: inout [UInt8]) { - values = KafkaArray(bytes: &bytes) - requestAcks = KafkaInt16(bytes: &bytes) - timeout = KafkaInt32(bytes: &bytes) + required init(data: inout Data) { + values = KafkaArray(data: &data) + requestAcks = Int16(data: &data) + timeout = Int32(data: &data) } - var length: Int { - return requestAcks.length + timeout.length + values.length + var dataLength: Int { + return requestAcks.dataLength + timeout.dataLength + values.dataLength } var data: Data { - var data = Data(capacity: length) + var data = Data(capacity: dataLength) data.append(requestAcks.data) data.append(timeout.data) @@ -75,170 +75,134 @@ class ProduceRequestMessage: KafkaClass { return data } - - var description: String { - return values.description - } } class ProduceResponse: KafkaResponse { + var data: Data { + return values.data + } + + var dataLength: Int { + return values.dataLength + } + var values: KafkaArray - required init(bytes: inout [UInt8]) { - values = KafkaArray(bytes: &bytes) - super.init(bytes: &bytes) - } - - override var description: String { - return values.description + required init(data: inout Data) { + values = KafkaArray(data: &data) } } -class TopicalResponse: KafkaClass { - - private var _topicName: KafkaString - private var _partitions: KafkaArray - - var topicName: String { - return _topicName.value ?? String() - } +class TopicalResponse: KafkaType { - var partitions: [PartitionedResponse] { - var values: [PartitionedResponse] = [] - - for partition in _partitions.values { - values.append(partition) - } - - return values - } + var topicName: TopicName + let partitions: KafkaArray - required init(bytes: inout [UInt8]) { - _topicName = KafkaString(bytes: &bytes) - _partitions = KafkaArray(bytes: &bytes) + required init(data: inout Data) { + topicName = TopicName(data: &data) + partitions = KafkaArray(data: &data) } - var length: Int { - return _topicName.length + _partitions.length + var dataLength: Int { + return topicName.dataLength + partitions.dataLength } var data: Data { return Data() } - - var description: String { - return "----------\nTOPICAL RESPONSE:\n" + - "\tTOPIC NAME: \(topicName)\n" + - "\tPARTITIONS: \(_partitions.description)" - } } -class PartitionedResponse: KafkaClass { - private var _partition: KafkaInt32 - private var _errorCode: KafkaInt16 - private var _offset: KafkaInt64 +class PartitionedResponse: KafkaType { + private var _partition: Int32 + private var _errorCode: Int16 + private var _offset: Int64 - required init(bytes: inout [UInt8]) { - _partition = KafkaInt32(bytes: &bytes) - _errorCode = KafkaInt16(bytes: &bytes) - _offset = KafkaInt64(bytes: &bytes) + required init(data: inout Data) { + _partition = Int32(data: &data) + _errorCode = Int16(data: &data) + _offset = Int64(data: &data) } var partition: Int32 { - return _partition.value + return _partition } var offset: Int64 { - return _offset.value + return _offset } var error: KafkaErrorCode? { - return KafkaErrorCode(rawValue: _errorCode.value) + return KafkaErrorCode(rawValue: _errorCode) } - var length: Int { - return _partition.length + _errorCode.length + _offset.length + var dataLength: Int { + return _partition.dataLength + _errorCode.dataLength + _offset.dataLength } var data: Data { return Data() } - - var description: String { - let errorDesscription = error?.description ?? "" - let errorCode = error?.code ?? 0 - - return "----------\nPARTITIONED RESPONSE:\n" + - "\tPARTITION: \(partition)\n" + - "\tERROR CODE: \(errorCode)\n" + - "\tERROR DESCRIPTION: \(errorDesscription)\n" + - "\tOFFSET: \(offset)" - } } -class KafkaTopicalMessageSet: KafkaClass { +class KafkaTopicalMessageSet: KafkaType { var values: KafkaArray - var topic: KafkaString + var topic: String init(values: [KafkaPartitionedMessageSet], topic: String) { - self.values = KafkaArray(values: values) - self.topic = KafkaString(value: topic) + self.values = KafkaArray(values) + self.topic = topic } - required init(bytes: inout [UInt8]) { - values = KafkaArray(bytes: &bytes) - topic = KafkaString(bytes: &bytes) + required init(data: inout Data) { + values = KafkaArray(data: &data) + topic = String(data: &data) } - var length: Int { - return topic.length + values.length + var dataLength: Int { + return topic.dataLength + values.dataLength } var data: Data { - var data = Data(capacity: length) + var data = Data(capacity: dataLength) data.append(topic.data) data.append(values.data) return data } - - var description: String { - return "\tTOPIC: \(topic.value ?? "nil") => \(topic.data)\n" + - values.description - } } -class KafkaPartitionedMessageSet: KafkaClass { +class KafkaPartitionedMessageSet: KafkaType { var value: MessageSet - var partition: KafkaInt32 + var partition: Int32 init(value: MessageSet, partition: Int32) { self.value = value - self.partition = KafkaInt32(value: partition) + self.partition = partition } - required init(bytes: inout [UInt8]) { - value = MessageSet(bytes: &bytes) - partition = KafkaInt32(bytes: &bytes) - } - - var length: Int { - return partition.length + value.length + required init(data: inout Data) { + value = MessageSet(data: &data) + partition = Int32(data: &data) } + + var messageSetSize: Int32 { + return Int32(value.dataLength) + } + + var dataLength: Int { + return partition.dataLength + messageSetSize.dataLength + value.dataLength + } var data: Data { - var data = Data(capacity: length) + var data = Data(capacity: dataLength) data.append(partition.data) + data.append(messageSetSize.data) data.append(value.data) return data } - - var description: String { - return "\t\tPARTITION: \(partition.value)\n\(value.description)" - } } diff --git a/Sources/Franz/Topic.swift b/Sources/Franz/Topic.swift index 6056be7..aa9691a 100644 --- a/Sources/Franz/Topic.swift +++ b/Sources/Franz/Topic.swift @@ -8,9 +8,10 @@ import Foundation +public typealias TopicName = String open class Topic: NSObject { - private var _name: String + private var _name: TopicName private var _partitions: [Int32] open var name: String { @@ -27,13 +28,13 @@ open class Topic: NSObject { } } -internal class KafkaTopic: KafkaClass { - private var _errorCode: KafkaInt16 - private var _topicName: KafkaString +internal class KafkaTopic: KafkaType { + private var _errorCode: Int16 + private var _topicName: String private var _partitionMetadata: KafkaArray var error: KafkaErrorCode? { - if let error = KafkaErrorCode(rawValue: _errorCode.value) { + if let error = KafkaErrorCode(rawValue: _errorCode) { return error } else { return nil @@ -51,41 +52,26 @@ internal class KafkaTopic: KafkaClass { } var name: String? { - return _topicName.value - } - - var description: String { - var description = """ - TOPIC METADATA - ERROR CODE: \(error?.code ?? 0) - ERROR DESCRIPTION: \(error?.description ?? "nil") - TOPIC: \(name ?? "nil") - """ - - for (_, partition) in partitions { - description += "----------\n\(partition.description)\n" - } - - return description + return _topicName } init(errorCode: Int, name: String, partitionMetadata: [Partition]) { - self._errorCode = KafkaInt16(value: Int16(errorCode)) - self._topicName = KafkaString(value: name) - self._partitionMetadata = KafkaArray(values: partitionMetadata) + self._errorCode = Int16(errorCode) + self._topicName = name + self._partitionMetadata = KafkaArray(partitionMetadata) } - required init(bytes: inout [UInt8]) { - _errorCode = KafkaInt16(bytes: &bytes) - _topicName = KafkaString(bytes: &bytes) - _partitionMetadata = KafkaArray(bytes: &bytes) + required init(data: inout Data) { + _errorCode = Int16(data: &data) + _topicName = String(data: &data) + _partitionMetadata = KafkaArray(data: &data) } - lazy var length: Int = { - return self._errorCode.length + lazy var dataLength: Int = { + return self._errorCode.dataLength }() var data: Data { - return Data() + return _errorCode.data + _topicName.data + _partitionMetadata.data } } diff --git a/Sources/Franz/Wildcard.swift b/Sources/Franz/Wildcard.swift deleted file mode 100644 index e8b159e..0000000 --- a/Sources/Franz/Wildcard.swift +++ /dev/null @@ -1,1023 +0,0 @@ -// -// Wildcard.swift -// KafkaClient -// -// Created by Kellan Cummings on 1/10/16. -// Copyright © 2016 Kellan Cummings. All rights reserved. -// - -import Foundation - -/** - Wrapper class for NSRegularExpression with convenience methods for common string-parsing operations - */ -class RegExp { - - private var pattern: String = "" - private var replacement: String = "" - private var options: UInt = 0 - private var mOptions: UInt = 0 - private var regExp: NSRegularExpression? - - /** - Initialize a new Regular Expression object with a pattern and options. The following flags are permitted: - - * i: case-insenstive match - * x: ignore #-prefixed comments and whitespace in this pattern - * s: `.` matches `\n` - * m: `^`, `$` match the beginning and end of lines, respectively (set by default) - * w: use unicode word boundaries - * c: ignore metacharacters when matching (e.g, `\w`, `\d`, `\s`, etc..) - * l: use only `\n` as a line separator - - - parameter pattern: an ICU-style regular expression - - parameter options: a string containing option flags - - */ - init(_ pattern: String, _ options: String = "") { - setOptions("\(options)m") - self.pattern = pattern - } - - /** - Counts the number of matches in a string - - - parameter input: an input string - - - returns: the number of matches in the input string - */ - func count(_ input: String) -> Int? { - let capacity = input.utf16.count - - if let regExp = doRegExp() { - return regExp.numberOfMatches( - in: input, - options: NSRegularExpression.MatchingOptions(rawValue: mOptions), - range: NSMakeRange( - 0, - capacity - ) - ) - } - - return nil - } - - /** - Looks for the first ICU-style pattern match in the input string - - - parameter input: an input string - - - returns: an array of matches or nil - */ - func match(_ input: String) -> [String]? { - var input = input - input = input.replacingOccurrences(of: "\n", with: "\\n", options: NSString.CompareOptions.literal, range: nil) - - var matches: [String] = [String]() - - getFirstMatch(input) { result in - - let numRanges = result.numberOfRanges - - for i in 0.. [[String]]? { - var input = input - input = input.replacingOccurrences(of: "\n", with: "\\n", options: NSString.CompareOptions.literal, range: nil) - - var matches: [[String]] = [[String]]() - - getMatches(input) { result, index in - - if matches.count - 1 < index { - matches.append([String]()) - } - - let numRanges = result.numberOfRanges - - for i in 0.. Void) { - if let regExp = doRegExp() { - var results = regExp.matches( - in: input, - options: NSRegularExpression.MatchingOptions(rawValue: mOptions), - range: input.toRange() - ) - - if reverse { - results = Array(results.reversed()) - } - - for (i, result) in results.enumerated() { - onMatch(result, i) - } - } - } - - private func getFirstMatch(_ input: String, onMatch: (NSTextCheckingResult) -> Void) { - if let regExp = doRegExp() { - - let range = makeRange(input) - - var results = regExp.matches( - in: input, - options: NSRegularExpression.MatchingOptions(rawValue: mOptions), - range: range - ) - - if results.count > 0 { - onMatch(results[0]) - } - } - } - - private func getMatches(_ input: String, onMatch: (NSTextCheckingResult, Int) -> Void) { - getAllMatches(input, reverse: false, onMatch: onMatch) - } - - private func getReverseMatches(_ input: String, onMatch: (NSTextCheckingResult, Int) -> Void) { - getAllMatches(input, reverse: true, onMatch: onMatch) - } - - //Substitution - internal func gsub(_ attributed: NSMutableAttributedString, _ replacement: String) -> NSMutableAttributedString { - return NSMutableAttributedString(string: gsub(attributed.mutableString, replacement) as String) - } - - /** - Substitute all matches in input string with replacement string - - - parameter input: an input string - - parameter replacement: replacement string (supports back references) - - - returns: the modified input string - */ - func gsub(_ string: String, _ replacement: String) -> String { - return gsub(string.toMutable(), replacement) as String - } - - internal func gsub(_ mutable: NSMutableString, _ replacement: String) -> NSMutableString { - self.replacement = replacement - if let regExp = doRegExp() { - regExp.replaceMatches( - in: mutable, - options: NSRegularExpression.MatchingOptions(rawValue: mOptions), - range: NSMakeRange(0, mutable.length), - withTemplate: self.replacement - ) - } - return mutable - } - - /** - Substitute all matches in input string with return value of callback function - - - parameter input: an input string - - parameter callback: a callback function that takes a match as an argument and returns a modified string (does not support back references) - - - returns: the modified input string - */ - func gsub(_ string: String, callback: ((String) -> (String))) -> String { - return gsub(string.toMutable(), callback: callback) as String - } - - internal func gsub(_ mutable: NSMutableString, callback: ((String) -> (String))) -> NSMutableString { - getReverseMatches(mutable as String) { result, index in - let numRanges = result.numberOfRanges - for i in 0.. String { - let mutable = string.toMutable() - - getFirstMatch(string) { result in - if let regExp = self.regExp { - - let substitute = regExp.replacementString( - for: result, - in: string, - offset: 0, - template: replacement - ) - - mutable.replaceCharacters( - in: result.range(at: 0), - with: substitute - ) - } - } - - return mutable as String - } - - /* Utility functions for finding substring ranges */ - private func makeRange(_ input: String) -> NSRange { - let capacity = input.utf16.count - return NSMakeRange(0, capacity) - } - - internal func getSubstringRanges(_ input: NSMutableAttributedString) -> [RegExpMatch]? { - return getSubstringRanges(input.mutableString as String) - } - - internal func getSubstringRanges(_ input: String) -> [RegExpMatch]? { - var matches = [RegExpMatch]() - - getMatches(input) { result, index in - let numRanges = result.numberOfRanges - let matchRange = result.range(at: 0) - let match = input.substringWithNSRange(matchRange) - - let regExpMatch: MatchTuple = (match, matchRange) - var regExpSubmatches: [MatchTuple] = [MatchTuple]() - - for i in 1.. 0 { - return matches - } - - return nil - } - - ///TODO: Find out what these do and use them or don't - private func setMatchingOptions(_ flags: String) -> UInt { - /* - NSMatchingOptions.ReportProgress - NSMatchingOptions.ReportCompletion - NSMatchingOptions.Anchored - NSMatchingOptions.WithTransparentBounds - NSMatchingOptions.WithoutAnchoringBounds - */ - mOptions = UInt(0) - return mOptions - } - - private func setOptions(_ flags: String) { - var options: UInt = 0 - - for character in flags.characters { - switch(character) { - case("i"): - options |= NSRegularExpression.Options.caseInsensitive.rawValue - case("x"): - options |= NSRegularExpression.Options.allowCommentsAndWhitespace.rawValue - case("s"): - options |= NSRegularExpression.Options.dotMatchesLineSeparators.rawValue - case("m"): - options |= NSRegularExpression.Options.anchorsMatchLines.rawValue - case("w"): - options |= NSRegularExpression.Options.useUnicodeWordBoundaries.rawValue - case("c"): - options |= NSRegularExpression.Options.ignoreMetacharacters.rawValue - case("l"): - options |= NSRegularExpression.Options.useUnixLineSeparators.rawValue - default: - options |= 0 - } - } - - self.options = options - } - - private func removeLinebreaks( _ input: inout String) { - input = input.replacingOccurrences(of: "\r\n", with: "\n", options: [.literal], range: nil) - } - - private func doRegExp() -> NSRegularExpression? { - - var error: NSError? - - do { - regExp = try NSRegularExpression( - pattern: pattern, - options: NSRegularExpression.Options(rawValue: options) - ) - } catch let error1 as NSError { - error = error1 - regExp = nil - } - - if let error = error { - print("!!Error: There was an problem matching `\(pattern )`: \(error)") - return nil - } else { - return regExp - } - } -} - -internal typealias MatchTuple = (string: String, range: NSRange) - -internal func ==(right: RegExpMatch, left: RegExpMatch) -> Bool { - return right.match.range.location == left.match.range.location - && right.match.range.length == left.match.range.length -} - -internal class RegExpMatch: Equatable { - var pattern: String - var match: MatchTuple - var submatches: [MatchTuple] - var subexpressions = [RegExpMatch]() - - internal init(pattern: String, match: MatchTuple, submatches: [MatchTuple]) { - self.pattern = pattern - self.submatches = submatches - self.match = match - } - - internal var subrange: NSRange { - get { - return submatches[0].range - } - set(range) { - submatches[0].range = range - } - } - - internal var substring: String { - return submatches[0].string - } - - internal var fullrange: NSRange { - get { - return match.range - } - - set(range) { - match.range = range - } - } - - internal var fullstring: String { - return match.string - } - - internal func addSubexpression(_ sub: RegExpMatch) { - - //println("\(sub.fullrange), \(sub.subrange): \(fullrange)") - sub.fullrange = NSRange( - location: sub.fullrange.location - fullrange.location, - length: sub.fullrange.length - ) - - sub.subrange = NSRange( - location: sub.subrange.location - fullrange.location, - length: sub.subrange.length - ) - - subexpressions.append(sub) - } - - internal func formatSubexpressions( _ replacement: inout NSMutableAttributedString) { - if subexpressions.count > 0 { - for sub in subexpressions { - if let matches = RegExp(sub.pattern).getSubstringRanges(replacement) { - - for match in matches { - let substring = NSMutableAttributedString( - string: match.substring - ) - - replacement.replaceCharacters( - in: match.fullrange, - with: substring - ) - } - } - } - } - } - - internal class func nest( _ sets: inout [RegExpMatch]) { - for setA in sets { - for setB in sets { - if setA != setB { - let intersection = NSIntersectionRange(setA.fullrange, setB.fullrange) - if intersection.location > 0 && intersection.length > 0 { - - if setA.fullrange.location <= setB.fullrange.location { - if let index = sets.index(of: setB) { - sets.remove(at: index) - setA.addSubexpression(setB) - } - } else { - if let index = sets.index(of: setA) { - sets.remove(at: index) - setB.addSubexpression(setA) - } - } - } - } - } - - if setA.subexpressions.count > 1 { - RegExpMatch.nest(&setA.subexpressions) - } - } - - sets.sort { - $0.fullrange.location > $1.fullrange.location - } - } -} - -private let consonant = "[b-df-hj-np-tv-z]" -private let vowel = "[aeiou]" - -let plurals: [(String, String)] = [ - ("(?<=f)oo(?=t)$|(?<=t)oo(?=th)$|(?<=g)oo(?=se)$", "ee"), - ("(?<=i)fe$|(?<=[eao]l)f$|(?<=(l|sh)ea)f$", "ves"), - ("(\\w{2,})[ie]x", "$1ices"), - ("(?<=[ml])ouse$", "ice"), - ("man$", "men"), - ("child$", "children"), - ("person$", "people"), - ("eau$", "eaux"), - ("(?<=-by)$", "s"), - ("(?<=[^q]\(vowel)y)$", "s"), - ("y$", "ies"), - ("(?<=s|sh|tch)$", "es"), - ("(?<=\(vowel)\(consonant)i)um", "a"), - ("(?<=\\w)$", "s") -] - -let singulars: [(String, String)] = [ - ("(?<=f)ee(?=t)$|(?<=t)ee(?=th)$|(?<=g)ee(?=se)$", "oo"), - ("(?<=i)ves$", "fe"), - ("(?<=[eao]l)ves$|(?<=(l|sh)ea)ves$", "f"), - ("(?<=[ml])ice$", "ouse"), - ("men$", "man"), - ("children$", "child"), - ("people$", "person"), - ("eaux$", "eau"), - ("(?<=-by)s$", ""), - ("(?<=[^q]\(vowel)y)s$", ""), - ("ies$", "y"), - ("(?<=s|sh|tch)es$", ""), - ("(?<=\(vowel)\(consonant)i)a", "um"), - ("(?<=\\w)s$", "") -] - -private let irregulars: [String:String] = [ - "potato": "potatoes", - "di": "dice", - "appendix": "appendices", - "index": "indices", - "matrix": "matrices", - "radix": "radices", - "vertex": "vertices", - "radius": "radii", - "goose": "geese" -] - -infix operator =~ : AdditionPrecedence - -/** - Checks if the input matches the pattern - - - parameter left: the input string - - parameter right: the pattern - - - returns: returns true if pattern exists in the input string - */ -func =~(left: String, right: String) -> Bool { - return left.match(right) != nil -} - -extension String { - - /** - Convert a string into an NSDate object. - Currently supports both backslashes and hyphens in the following formats: - - * Y-m-d - * m-d-Y - * Y-n-j - * n-j-Y - - - returns: a date - */ - public func toDate() -> Date? { - //println("to Date: \(self)") - - let patterns = [ - "\\w+ (\\w+) (\\d+) (\\d{1,2}):(\\d{1,2}):(\\d{1,2}) \\+\\d{4} (\\d{4})": [ - "month", "day", "hour", "minute", "second", "year" - ], - "(\\d{4})[-\\/](\\d{1,2})[-\\/](\\d{1,2})(?: (\\d{1,2}):(\\d{1,2}):(\\d{1,2}))?": [ - "year", "month", "day", "hour", "minute", "second" - ], - "(\\d{1,2})[-\\/](\\d{1,2})[-\\/](\\d{4})(?: (\\d{1,2}):(\\d{1,2}):(\\d{1,2}))?": [ - "month", "day", "year", "hour", "minute", "second" - ] - ] - - let months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] - - for (pattern, map) in patterns { - if let matches = self.match(pattern) { - //println("Matches \(matches)") - if(matches.count >= 4) { - var dictionary = [String:String]() - - for (i, item) in map.enumerated() { - if i + 1 < matches.count { - dictionary[item] = matches[i + 1] - } else { - break - } - } - - let calendar = Calendar.current - var comp = DateComponents() - - comp.year = 0 - if let year_string = dictionary["year"], - let year = Int(year_string) - { - comp.year = year - } - - comp.month = 0 - if let month = dictionary["month"] { - if let month = Int(month) { - comp.month = month - } else { - for (i, m) in months.enumerated() { - if month =~ m { - comp.month = i - break - } - } - } - } - - comp.day = 0 - if let day_string = dictionary["day"], let day = Int(day_string) { - comp.day = day - } - - comp.hour = 0 - if let hour_string = dictionary["hour"], let hour = Int(hour_string) { - comp.hour = hour - } - - comp.minute = 0 - if let minute_string = dictionary["minute"], let minute = Int(minute_string) { - comp.minute = minute - } - - comp.second = 0 - if let second_string = dictionary["second"], let second = Int(second_string) { - comp.second = second - } - - return calendar.date(from: comp) - } - } - } - return nil - } - - /** - Split a string into an array of strings by slicing at delimiter - - - parameter delimiter: character(s) to split string at - - - returns: an array of strings if delimiter matches, or an array - with the original string as its only component - */ - func split(_ delimiter: String) -> [String] { - let parsedDelimiter: String = NSRegularExpression.escapedPattern(for: delimiter) - - if let matches = self.scan("(.+?)(?:\(parsedDelimiter)|$)") { - var arr = [String]() - for match in matches { - arr.append(match[1]) - } - - return arr - } else { - return [self] - } - } - - /** - Substitute result of callback function for all occurences of pattern - - - parameter pattern: a regular expression string to match against - - parameter callback: a callback function to call on pattern match success - - - returns: modified string - */ - func gsub(_ pattern: String, callback: ((String) -> (String))) -> String { - let regex = RegExp(pattern) - return regex.gsub(self, callback: callback) - } - - /** - Substitute result of callback function for all occurences of pattern. - The following flags are permitted: - - * i: case-insenstive match - * x: ignore #-prefixed comments and whitespace in this pattern - * s: `.` matches `\n` - * m: `^`, `$` match the beginning and end of lines, respectively (set by default) - * w: use unicode word boundaries - * c: ignore metacharacters when matching (e.g, `\w`, `\d`, `\s`, etc..) - * l: use only `\n` as a line separator - - - parameter pattern: an ICU-style regular expression - - parameter options: a string containing option flags - - parameter callback: a callback function to call on pattern match success - - - returns: modified string - */ - func gsub(_ pattern: String, options: String, callback: ((String) -> (String))) -> String { - let regex = RegExp(pattern, options) - return regex.gsub(self, callback: callback) - } - - /** - Convenience wrapper for gsub with options - */ - func gsub(_ pattern: String, _ replacement: String, options: String = "") -> String { - let regex = RegExp(pattern, options) - return regex.gsub(self, replacement) - } - - /** - Convenience wrapper for case-insenstive gsub - */ - func gsubi(_ pattern: String, _ replacement: String, options: String = "") -> String { - let regex = RegExp(pattern, "\(options)i") - return regex.gsub(self, replacement) - } - - /** - Convenience wrapper for case-insensitive gsub with callback - */ - func gsubi(_ pattern: String, callback: ((String) -> (String))) -> String { - let regex = RegExp(pattern, "i") - return regex.gsub(self, callback: callback) - } - - /** - Convenience wrapper for case-insensitive gsub with callback and options - */ - func gsubi(_ pattern: String, options: String, callback: ((String) -> (String))) -> String { - let regex = RegExp(pattern, "\(options)i") - return regex.gsub(self, callback: callback) - } - - - /** - Conveneience wrapper for first-match-only substitution - */ - func sub(_ pattern: String, _ replacement: String, options: String = "") -> String { - let regex = RegExp(pattern, options) - return regex.sub(self, replacement) - } - - /** - Conveneience wrapper for case-insensitive first-match-only substitution - */ - func subi(_ pattern: String, _ replacement: String, options: String = "") -> String { - let regex = RegExp(pattern, "\(options)i") - return regex.sub(self, replacement) - } - - /** - Scans and matches only the first pattern - - - parameter pattern: the pattern to search against - - parameter (not-required): options for matching--see documentation for `gsub`; defaults to "" - - - returns: an array of all matches to the first pattern - */ - func match(_ pattern: String, _ options: String = "") -> [String]? { - return RegExp(pattern, options).match(self) - } - - /** - Scans and matches all patterns - - - parameter pattern: the pattern to search against - - parameter (not-required): options for matching--see documentation for `gsub`; defaults to "" - - - returns: an array of arrays of each matched pattern - */ - func scan(_ pattern: String, _ options: String = "") -> [[String]]? { - return RegExp(pattern, options).scan(self) - } - - /** - Slices out the parts of the string that match the pattern - - - parameter pattern: the pattern to search against - - - returns: an array of the slices - */ - mutating func slice(_ pattern: String) -> [[String]]? { - let matches = self.scan(pattern) - self = self.gsub(pattern, "") - return matches - } - - /** - Strip white space or aditional specified characters from beginning or end of string - - - parameter a: string of any characters additional characters to strip off beginning/end of string - - - returns: trimmed string - */ - func trim(_ characters: String = "") -> String { - let parsedCharacters = NSRegularExpression.escapedPattern(for: characters) - return self.gsub("^[\\s\(parsedCharacters)]+|[\\s\(parsedCharacters)]+$", "") - } - - /** - Strip white space or aditional specified characters from end of string - - - parameter a: string of any characters additional characters to strip off end of string - - - returns: trimmed string - */ - func rtrim(_ characters: String = "") -> String { - let parsedCharacters = NSRegularExpression.escapedPattern(for: characters) - return self.gsub("[\\s\(parsedCharacters)]+$", "") - } - - /** - Strip white space or aditional specified characters from beginning of string - - - parameter a: string of any characters additional characters to strip off beginning of string - - - returns: trimmed string - */ - func ltrim(_ characters: String = "") -> String { - let parsedCharacters = NSRegularExpression.escapedPattern(for: characters) - return self.gsub("^[\\s\(parsedCharacters)]+", "") - } - - /** - Converts Html special characters (e.g. '©' => '©') - - - returns: converted string - */ - func decodeHtmlSpecialCharacters() -> String { - let regex = RegExp("&#[a-fA-F\\d]+;") - - return regex.gsub(self) { pattern in - let hex = RegExp("[a-fA-F\\d]+") - if let matches = hex.match(pattern) { - if let sint = Int(matches[0]) { - let character = Character(UnicodeScalar(UInt32(sint))!) - return "\(character)" - } - } - print("There was an issue while trying to decode character '\(pattern)'") - return "" - } - } - - /** - Converts a string to camelcase. e.g.: 'hello_world' -> 'HelloWorld' - - - returns: a formatted string - */ - func toCamelcase() -> String { - return gsub("[_\\-\\s]\\w") { match in - return match[match.characters.index(match.startIndex, offsetBy: 1).. 'hello_world' - - - parameter language: (Reserved for future use) - - - returns: a formatted string - */ - func toSnakecase() -> String { - return gsub("[\\s-]\\w") { match in - return "_" + match[match.characters.index(match.startIndex, offsetBy: 1).. String { - if let plural = irregulars[self] { - return plural - } - - for (regex, mod) in plurals { - let replacement = self.gsubi(regex, mod) - if replacement != self { - return replacement - } - } - - return self - } - - /** - DEVELOPMENTAL METHOD: Change String from plural to singular. - - - returns: a singular string - */ - func singularize(_ language: String = "en/us") -> String { - if let plurals = irregulars.flip(), let plural = plurals[self] { - return plural - } - - for (regex, mod) in singulars { - let replacement = self.gsubi(regex, mod) - if replacement != self { - return replacement - } - } - - return self - } - - /** - Set the first letter to lowercase - - - returns: formatted string - */ - func decapitalize() -> String { - let prefix = self[startIndex.. String { - let prefix = self[startIndex.. String { - - var rstring = "" - if times > 0 { - for _ in 0...times { - rstring = "\(rstring)\(self)" - } - } - return rstring - } - - internal func substringWithNSRange(_ range: NSRange) -> String { - return substring(with: range.toStringIndexRange(self)) - } - - internal func substringRanges(_ pattern: String, _ options: String = "") -> [RegExpMatch]? { - return RegExp(pattern, options).getSubstringRanges(self) - } - - internal func toMutable() -> NSMutableString { - let capacity = self.utf16.count - let mutable = NSMutableString(capacity: capacity) - mutable.append(self) - return mutable - } - - internal func toRange() -> NSRange { - let capacity = self.utf16.count - return NSMakeRange(0, capacity) - } -} - -internal extension NSMutableString { - internal func gsub(_ pattern: String, _ replacement: String) -> NSMutableString { - let regex = RegExp(pattern) - return regex.gsub(self, replacement) - } - - internal func substringRanges(_ pattern: String, _ options: String = "") -> [RegExpMatch]? { - return RegExp(pattern, options).getSubstringRanges(self as String) - } -} - -internal extension NSMutableAttributedString { - internal func substringRanges(_ pattern: String, _ options: String = "") -> [RegExpMatch]? { - return RegExp(pattern, options).getSubstringRanges(self) - } -} - -internal extension NSRange { - internal func toStringIndexRange(_ input: String) -> Range { - if location < input.utf16.count { - let startIndex = input.characters.index(input.startIndex, offsetBy: location) - let endIndex = input.characters.index(input.startIndex, offsetBy: location + length) - let range = (startIndex ..< endIndex) - //println(input.substringWithRange(range)) - return range - } - - return (input.characters.indices.startIndex ..< input.characters.indices.endIndex) - } -} - -internal extension Dictionary { - - internal func flip() -> Dictionary? { - if Key.self is Value.Type { - var out = Dictionary() - - for key in self.keys { - if let value = self[key] as? Key, let key = key as? Value { - out[value] = key - } - } - - return out.count > 0 ? out : nil - } - - return nil - } -} diff --git a/Swift package manager example/Franz Example/AppDelegate.swift b/Swift package manager example/Franz Example/AppDelegate.swift deleted file mode 100644 index 32698ef..0000000 --- a/Swift package manager example/Franz Example/AppDelegate.swift +++ /dev/null @@ -1,46 +0,0 @@ -// -// AppDelegate.swift -// Franz Example -// -// Created by Luke Lau on 02/07/2017. -// Copyright © 2017 Luke Lau. All rights reserved. -// - -import UIKit - -@UIApplicationMain -class AppDelegate: UIResponder, UIApplicationDelegate { - - var window: UIWindow? - - - func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { - // Override point for customization after application launch. - return true - } - - func applicationWillResignActive(_ application: UIApplication) { - // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. - // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. - } - - func applicationDidEnterBackground(_ application: UIApplication) { - // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. - // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. - } - - func applicationWillEnterForeground(_ application: UIApplication) { - // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. - } - - func applicationDidBecomeActive(_ application: UIApplication) { - // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. - } - - func applicationWillTerminate(_ application: UIApplication) { - // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. - } - - -} - diff --git a/Swift package manager example/Franz Example/Assets.xcassets/AppIcon.appiconset/Contents.json b/Swift package manager example/Franz Example/Assets.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index 1d060ed..0000000 --- a/Swift package manager example/Franz Example/Assets.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -1,93 +0,0 @@ -{ - "images" : [ - { - "idiom" : "iphone", - "size" : "20x20", - "scale" : "2x" - }, - { - "idiom" : "iphone", - "size" : "20x20", - "scale" : "3x" - }, - { - "idiom" : "iphone", - "size" : "29x29", - "scale" : "2x" - }, - { - "idiom" : "iphone", - "size" : "29x29", - "scale" : "3x" - }, - { - "idiom" : "iphone", - "size" : "40x40", - "scale" : "2x" - }, - { - "idiom" : "iphone", - "size" : "40x40", - "scale" : "3x" - }, - { - "idiom" : "iphone", - "size" : "60x60", - "scale" : "2x" - }, - { - "idiom" : "iphone", - "size" : "60x60", - "scale" : "3x" - }, - { - "idiom" : "ipad", - "size" : "20x20", - "scale" : "1x" - }, - { - "idiom" : "ipad", - "size" : "20x20", - "scale" : "2x" - }, - { - "idiom" : "ipad", - "size" : "29x29", - "scale" : "1x" - }, - { - "idiom" : "ipad", - "size" : "29x29", - "scale" : "2x" - }, - { - "idiom" : "ipad", - "size" : "40x40", - "scale" : "1x" - }, - { - "idiom" : "ipad", - "size" : "40x40", - "scale" : "2x" - }, - { - "idiom" : "ipad", - "size" : "76x76", - "scale" : "1x" - }, - { - "idiom" : "ipad", - "size" : "76x76", - "scale" : "2x" - }, - { - "idiom" : "ipad", - "size" : "83.5x83.5", - "scale" : "2x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Swift package manager example/Franz Example/Base.lproj/LaunchScreen.storyboard b/Swift package manager example/Franz Example/Base.lproj/LaunchScreen.storyboard deleted file mode 100644 index fdf3f97..0000000 --- a/Swift package manager example/Franz Example/Base.lproj/LaunchScreen.storyboard +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Swift package manager example/Franz Example/Base.lproj/Main.storyboard b/Swift package manager example/Franz Example/Base.lproj/Main.storyboard deleted file mode 100644 index 273375f..0000000 --- a/Swift package manager example/Franz Example/Base.lproj/Main.storyboard +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Swift package manager example/Franz Example/Info.plist b/Swift package manager example/Franz Example/Info.plist deleted file mode 100644 index 16be3b6..0000000 --- a/Swift package manager example/Franz Example/Info.plist +++ /dev/null @@ -1,45 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - APPL - CFBundleShortVersionString - 1.0 - CFBundleVersion - 1 - LSRequiresIPhoneOS - - UILaunchStoryboardName - LaunchScreen - UIMainStoryboardFile - Main - UIRequiredDeviceCapabilities - - armv7 - - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - - diff --git a/Swift package manager example/Franz Example/ViewController.swift b/Swift package manager example/Franz Example/ViewController.swift deleted file mode 100644 index 8869fd5..0000000 --- a/Swift package manager example/Franz Example/ViewController.swift +++ /dev/null @@ -1,104 +0,0 @@ -// -// ViewController.swift -// Franz Example -// -// Created by Luke Lau on 02/07/2017. -// Copyright © 2017 Luke Lau. All rights reserved. -// - -import UIKit -import Franz - -class ViewController: UIViewController, HighLevelConsumerDelegate { - - func consumerDidReturnMessage(_ message: Message, offset: Int64) { - print(String(data: message.value, encoding: .utf8) ?? "Couldn't decode message") - } - - func consumerIsReady(_ consumer: Consumer) { - print("Consumer is ready") - if let highLevelConsumer = consumer as? HighLevelConsumer { - highLevelConsumer.poll() - } - } - - var cluster: Cluster! - - override func viewDidLoad() { - super.viewDidLoad() - // Do any additional setup after loading the view, typically from a nib. - - cluster = Cluster( - brokers: [ - ("localhost", 9092) - ], - clientId: "replica-test" - ) - - _ = cluster.getHighLevelConsumer("replica", partition: 0, groupId: "replica-group", delegate: self) - - DispatchQueue(label: "testMessages").async { - while true { - DispatchQueue.main.sync { - self.cluster.batchMessage("replica", partition: 0, message: "1") - self.cluster.batchMessage("replica", partition: 0, message: "2") - self.cluster.batchMessage("replica", partition: 0, message: "3") - - do { - try self.cluster.sendBatch("replica", partition: 0) - } catch ClusterError.noBatchForTopicPartition(let topic, let partition) { - print("Error: No batch for topic \(topic), \(partition)") - } catch { - print("Error") - } - } - sleep(1) - } - } - } - - override func didReceiveMemoryWarning() { - super.didReceiveMemoryWarning() - // Dispose of any resources that can be recreated. - } - - func topicPartitionLeaderNotFound(_ topic: String, partition: Int32) { - print("Topic Partition Leader Not Found") - } - - func shouldCommitOffset(_ topic: String, partition: Int32, offset: Int64) -> Bool { - print("Should Commit Offset") - return true - } - - func shouldAttachOffsetMetadata(_ topic: String, partition: Int32, offset: Int64) -> String? { - print("Should Attach Offset Metadata") - return "mooser" - } - - func offsetDidCommit(_ topic: String, partition: Int32, offset: Int64) { - print("Offset Did Commit") - } - - func offsetCommitDidFail( - _ topic: String, - partition: Int32, - offset: Int64, - errorId: Int16, - errorDescription: String - ) { - print("Offset Commit Did Fail") - } - - func shouldRetryFailedOffsetCommit( - _ topic: String, - partition: Int32, - offset: Int64, - errorId: Int16, - errorDescription: String - ) -> Bool { - return false - } - -} - diff --git a/Swift package manager example/Franz/Info.plist b/Swift package manager example/Franz/Info.plist deleted file mode 100644 index 1007fd9..0000000 --- a/Swift package manager example/Franz/Info.plist +++ /dev/null @@ -1,24 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - FMWK - CFBundleShortVersionString - 1.0 - CFBundleVersion - $(CURRENT_PROJECT_VERSION) - NSPrincipalClass - - - diff --git a/Swift package manager example/Package.resolved b/Swift package manager example/Package.resolved index 35f985d..7f10187 100644 --- a/Swift package manager example/Package.resolved +++ b/Swift package manager example/Package.resolved @@ -3,10 +3,10 @@ "pins": [ { "package": "Franz", - "repositoryURL": "/Users/luke/Documents/franz", + "repositoryURL": "/Users/luke/Repos/franz", "state": { "branch": null, - "revision": "d648f4868d0b449c37fd5fac5e783e66cf35cc1b", + "revision": "ecfecf72539cb172c115d41f05ea9eab8d1a6e40", "version": "0.2.0" } } diff --git a/Swift package manager example/Package.swift b/Swift package manager example/Package.swift index 18a5ab7..6cd02ae 100644 --- a/Swift package manager example/Package.swift +++ b/Swift package manager example/Package.swift @@ -4,17 +4,19 @@ import PackageDescription let package = Package( - name: "Franz Example", + name: "FranzExample", + products: [ + .executable(name: "FranzExample", targets: ["FranzExample"]) + ], dependencies: [ // Dependencies declare other packages that this package depends on. - .package(url: "../", from: "0.2.0"), + .package(url: "../", from: "0.2.0"), ], targets: [ // Targets are the basic building blocks of a package. A target can define a module or a test suite. // Targets can depend on other targets in this package, and on products in packages which this package depends on. .target( - name: "Franz Example", - dependencies: [], - path: "Franz Example"), + name: "FranzExample", + dependencies: ["Franz"]) ] ) diff --git a/Swift package manager example/README.md b/Swift package manager example/README.md index b27cc0c..a74bb97 100644 --- a/Swift package manager example/README.md +++ b/Swift package manager example/README.md @@ -1,5 +1,5 @@ -# Franz Example with Swift Package Manager +# Swift Package Manager Example -Follow [this guide](https://gist.github.com/nathanborror/4ecb16bcb1ae0bd11e7eed043c6cd8bd) to set it up with your own iOS/MacOS project. +To try it out, just run `swift run`. -To get this example to build from Xcode, first run `swift package restore`. +If you are building a command line application, you must call `RunLoop.main.run()` to allow Franz to schedule the socket streams. diff --git a/Swift package manager example/Sources/FranzExample/main.swift b/Swift package manager example/Sources/FranzExample/main.swift new file mode 100644 index 0000000..caa3c7b --- /dev/null +++ b/Swift package manager example/Sources/FranzExample/main.swift @@ -0,0 +1,13 @@ +import Foundation +import Franz + +let cluster = Cluster(brokers: [("localhost", 9092)], clientId: "FranzExample") + +let consumer = cluster.getConsumer(topics: ["test"], groupId: "group") +consumer.listen { message in + print(String(data: message.value, encoding: .utf8)!) +} + +cluster.sendMessage("test", message: "Hello world!") + +RunLoop.main.run() diff --git a/Tests/FranzTests/AssignmentTests.swift b/Tests/FranzTests/AssignmentTests.swift new file mode 100644 index 0000000..f235e12 --- /dev/null +++ b/Tests/FranzTests/AssignmentTests.swift @@ -0,0 +1,53 @@ +// +// AssignmentTests.swift +// FranzTests +// +// Created by Luke Lau on 23/07/2017. +// + +import XCTest +@testable import Franz + +class AssignmentTests: XCTestCase { + + func getMockPartition(id: Int) -> Partition { + return Partition(partitionErrorCode: 0, partitionId: id, leader: 0, replicas: [Int](), isr: [Int]()) + } + + func testRoundRobin() { + + let topics = ["foo", "bar", "baz", "daz", "waz", "woz"] + let partitions = topics.reduce([TopicName: [Partition]]()) { acc, next in + var newAcc = acc + newAcc[next] = [0, 1, 2, 3, 4, 5].map(getMockPartition) + return newAcc + } + + let cluster = Cluster(brokers: [(String, Int32)](), clientId: "test") + + let members = ["gary", "bary", "dary"] + + let result = cluster.assignRoundRobin(members: members, partitions: partitions) + + XCTAssertEqual(result["gary"]!["foo"]!, [0, 3]) + + let combinations = result.flatMap { member, topics in + topics.flatMap { topic, partitions in + partitions.map { partition in + (member, topic, partition) + } + } + } + + var seen = [(MemberId, TopicName, PartitionId)]() + for combination in combinations { + if seen.contains(where: { memberId, topicName, partitionId in + (memberId, topicName, partitionId) == combination + }) { + XCTFail("A partition was given out twice") + } + seen.append(combination) + } + } + +} diff --git a/Tests/FranzTests/CRCTests.swift b/Tests/FranzTests/CRCTests.swift index b5cbb5e..827c506 100644 --- a/Tests/FranzTests/CRCTests.swift +++ b/Tests/FranzTests/CRCTests.swift @@ -15,5 +15,5 @@ class CRCTests: XCTestCase { let crc = CRC32(data: data).crc XCTAssertEqual(0x88bed727, crc) } - + } diff --git a/Tests/FranzTests/FranzTests.swift b/Tests/FranzTests/FranzTests.swift deleted file mode 100644 index 99c4981..0000000 --- a/Tests/FranzTests/FranzTests.swift +++ /dev/null @@ -1,15 +0,0 @@ -import XCTest -@testable import Franz - -class FranzTests: XCTestCase { - func testClusterClientId() { - let cluster = Cluster(brokers: [("192.0.0.1", 9092)], clientId: "Test") - - XCTAssertEqual("Test", cluster.clientId) - } - - - static var allTests = [ - ("testClusterClientId", testClusterClientId), - ] -} diff --git a/Tests/FranzTests/Protocol/ArrayProtocolTests.swift b/Tests/FranzTests/Protocol/ArrayProtocolTests.swift new file mode 100644 index 0000000..747ddc7 --- /dev/null +++ b/Tests/FranzTests/Protocol/ArrayProtocolTests.swift @@ -0,0 +1,42 @@ +// +// ArrayProtocolTests.swift +// FranzTests +// +// Created by Luke Lau on 16/08/2017. +// + +import XCTest +@testable import Franz + +class ArrayProtocolTests: XCTestCase { + + func testArray() { + let values: [Int64] = [64, 32, 128] + + let array = KafkaArray(values) + + XCTAssertEqual(8 * 3 + 4, array.dataLength) + + var data = array.data + XCTAssertEqual(array.dataLength, data.count) + + let result = KafkaArray(data: &data) + XCTAssertEqual(values, Array(result)) + } + + func testEmptyArray() { + let values = [Int64]() + + let array = KafkaArray() + + XCTAssertEqual(4, array.dataLength) + + XCTAssertEqual(Data(bytes: [0, 0, 0, 0]), array.data) + + var data = array.data + + let result = KafkaArray(data: &data) + XCTAssertEqual(values, Array(result)) + } + +} diff --git a/Tests/FranzTests/Protocol/IntegerProtocolTests.swift b/Tests/FranzTests/Protocol/IntegerProtocolTests.swift new file mode 100644 index 0000000..87370d6 --- /dev/null +++ b/Tests/FranzTests/Protocol/IntegerProtocolTests.swift @@ -0,0 +1,106 @@ +// +// KafkaProtocolTests.swift +// FranzTests +// +// Created by Luke Lau on 14/08/2017. +// + +import XCTest +@testable import Franz + +class IntegerProtocolTests: XCTestCase { + + //TODO: Rewrite using generic integer type array once SE-0104 is implemented + //https://github.com/apple/swift-evolution/blob/master/proposals/0104-improved-integers.md + func testUInt8() { + for value in [42, UInt8.max, UInt8.min, 0] { + let int = UInt8(value) + var data = int.data + let recreated = UInt8(data: &data) + XCTAssertEqual(int, recreated) + + } + } + + func testUInt16() { + for value in [42, 0, UInt16.max, UInt16.min] { + let int = UInt16(value) + var data = int.data + let recreated = UInt16(data: &data) + XCTAssertEqual(int, recreated) + } + } + + func testUInt32() { + for value in [42, 0, UInt32.max, UInt32.min] { + let int = UInt32(value) + var data = int.data + let recreated = UInt32(data: &data) + XCTAssertEqual(int, recreated) + } + } + + func testUInt64() { + for value in [42, 0, UInt64.max, UInt64.min] { + let int = UInt64(value) + var data = int.data + let recreated = UInt64(data: &data) + XCTAssertEqual(int, recreated) + } + } + + func testUInt() { + for value in [42, 0, UInt.max, UInt.min] { + let int = UInt(value) + var data = int.data + let recreated = UInt(data: &data) + XCTAssertEqual(int, recreated) + } + } + + func testInt8() { + for value in [42, -42, 0, Int8.max, Int8.min] { + let int = Int8(value) + var data = int.data + let recreated = Int8(data: &data) + XCTAssertEqual(int, recreated) + } + } + + func testInt16() { + for value in [42, -42, 0, Int16.max, Int16.min] { + let int = Int16(value) + var data = int.data + let recreated = Int16(data: &data) + XCTAssertEqual(int, recreated) + } + } + + func testInt32() { + for value in [42, -42, 0, Int32.max, Int32.min] { + let int = Int32(value) + var data = int.data + let recreated = Int32(data: &data) + XCTAssertEqual(int, recreated) + } + } + + func testInt64() { + for value in [42, -42, 0, Int64.max, Int64.min] { + let int = Int64(value) + var data = int.data + let recreated = Int64(data: &data) + XCTAssertEqual(int, recreated) + } + } + + func testInt() { + for value in [42, -42, 0, Int.max, Int.min] { + let int = Int64(value) + var data = int.data + let recreated = Int64(data: &data) + XCTAssertEqual(int, recreated) + } + } + +} diff --git a/Tests/FranzTests/Protocol/MessageTests.swift b/Tests/FranzTests/Protocol/MessageTests.swift new file mode 100644 index 0000000..24b7587 --- /dev/null +++ b/Tests/FranzTests/Protocol/MessageTests.swift @@ -0,0 +1,32 @@ +// +// MessageTests.swift +// FranzTests +// +// Created by Luke Lau on 21/08/2017. +// + +import XCTest +@testable import Franz + +class MessageTests: XCTestCase { + + func testData() { + let message = KafkaMessage(value: "test", key: "foo") + var data = message.data + let decodedMessage = KafkaMessage(data: &data) + XCTAssertEqual("test", String(data: decodedMessage.value, encoding: .utf8)!) + XCTAssertEqual("foo", String(data: decodedMessage.key!, encoding: .utf8)!) + } + + func testMessageSet() { + let messageSet = MessageSet(values: [MessageSetItem(value: "foo", key: "bar", offset: 42)]) + var data = messageSet.data + + let decodedMessageSet = MessageSet(data: &data) + XCTAssertEqual(1, decodedMessageSet.values.count) + XCTAssertEqual(42, decodedMessageSet.values[0].offset) + XCTAssertEqual("foo".data(using: .utf8), decodedMessageSet.values[0].message.value) + XCTAssertEqual("bar".data(using: .utf8), decodedMessageSet.values[0].message.key!) + } + +} diff --git a/Tests/FranzTests/Protocol/VariableLengthProtocolTests.swift b/Tests/FranzTests/Protocol/VariableLengthProtocolTests.swift new file mode 100644 index 0000000..6d06274 --- /dev/null +++ b/Tests/FranzTests/Protocol/VariableLengthProtocolTests.swift @@ -0,0 +1,61 @@ +// +// VariableLengthProtocolTests.swift +// FranzTests +// +// Created by Luke Lau on 15/08/2017. +// + +import XCTest +@testable import Franz + +class VariableLengthProtocolTests: XCTestCase { + + func testDataTake() { + var data = Data(bytes: [0xff, 0xee, 0xdd, 0xcc]) + + let taken = data.take(first: 1) + + XCTAssertEqual(Data(bytes: [0xff]), taken) + + XCTAssertEqual(Data(bytes: [0xee, 0xdd, 0xcc]), data) + + let secondTaken = data.take(first: 2) + + XCTAssertEqual(Data(bytes: [0xee, 0xdd]), secondTaken) + + XCTAssertEqual(Data(bytes: [0xcc]), data) + } + + func testString() { + let s = "Hello world!" + var data = s.data + XCTAssertEqual(s, String(data: &data)) + } + + func testEmptyString() { + let s = "" + var data = s.data + XCTAssertEqual(s, String(data: &data)) + } + + func testOptionalString() { + let s: String? = nil + var data = s.data + XCTAssertEqual("", String(data: &data)) + + let s2: String? = "test" + var data2 = s2.data + XCTAssertEqual("test", String(data: &data2)) + } + + func testOptionalData() { + let d: Data? = nil + var data = d.data + XCTAssertEqual(Data(), Data(data: &data)) + + let d2: Data? = "test".data(using: .utf8) + var data2 = d2.data + XCTAssertEqual(d2, Data(data: &data2)) + } + +}