Compare commits
33 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 90e96addec | |||
| 100e2c98f4 | |||
| ee53b3f736 | |||
| 90ee7eac79 | |||
| f84e3ef366 | |||
| 060efc0c83 | |||
| 451fc6aa7f | |||
| 10e81534c7 | |||
| a03a974df9 | |||
| 1629110349 | |||
| 99e06e67db | |||
| 874707e958 | |||
| 78a514ed18 | |||
| e93483ea53 | |||
| ab78379716 | |||
| 6c182a887b | |||
| 55b822bd51 | |||
| 2567a56e2e | |||
| 628ab05ebc | |||
| 40afc59c8f | |||
| 7f4d30b205 | |||
| ce5073fcd8 | |||
| 182b4f0b1c | |||
| 6644db05d7 | |||
| 6c4574164e | |||
| bdae7ebd76 | |||
| 36a361d021 | |||
| f9db94fa11 | |||
| e59fadd0fb | |||
| 45f9930941 | |||
| 9266ae3689 | |||
| cd55374ea8 | |||
| 6c8cf96e65 |
@ -39,7 +39,7 @@
|
||||
/* Begin PBXFileReference section */
|
||||
0ABF8340F735722CEE937FA3 /* libPods-PNObject_Example.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-PNObject_Example.a"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
116AF818F8117B91EF290C9A /* Pods-PNObject_Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PNObject_Example.release.xcconfig"; path = "Pods/Target Support Files/Pods-PNObject_Example/Pods-PNObject_Example.release.xcconfig"; sourceTree = "<group>"; };
|
||||
2661CEE1F223361CD1DACC25 /* PNObject.podspec */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = PNObject.podspec; path = ../PNObject.podspec; sourceTree = "<group>"; };
|
||||
2661CEE1F223361CD1DACC25 /* PNObject.podspec */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = PNObject.podspec; path = ../PNObject.podspec; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.ruby; };
|
||||
3E392C278361604C1602CAF0 /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = "<group>"; };
|
||||
6003F58A195388D20070C39A /* PNObject_Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = PNObject_Example.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
6003F58D195388D20070C39A /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
|
||||
@ -210,7 +210,6 @@
|
||||
6003F586195388D20070C39A /* Sources */,
|
||||
6003F587195388D20070C39A /* Frameworks */,
|
||||
6003F588195388D20070C39A /* Resources */,
|
||||
C40CC8D7B1996A567BE82827 /* [CP] Embed Pods Frameworks */,
|
||||
614EA73ED5E0ED0F8684468C /* [CP] Copy Pods Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
@ -230,8 +229,6 @@
|
||||
6003F5AA195388D20070C39A /* Sources */,
|
||||
6003F5AB195388D20070C39A /* Frameworks */,
|
||||
6003F5AC195388D20070C39A /* Resources */,
|
||||
746A765B48FD98342716D81F /* [CP] Embed Pods Frameworks */,
|
||||
21411FBE0FE21EC75B6AB1E3 /* [CP] Copy Pods Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
@ -250,7 +247,7 @@
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
CLASSPREFIX = PNObj;
|
||||
LastUpgradeCheck = 0820;
|
||||
LastUpgradeCheck = 1120;
|
||||
ORGANIZATIONNAME = "Giuseppe Nucifora";
|
||||
TargetAttributes = {
|
||||
6003F589195388D20070C39A = {
|
||||
@ -271,6 +268,7 @@
|
||||
developmentRegion = English;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
English,
|
||||
en,
|
||||
Base,
|
||||
);
|
||||
@ -307,21 +305,6 @@
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXShellScriptBuildPhase section */
|
||||
21411FBE0FE21EC75B6AB1E3 /* [CP] Copy Pods Resources */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "[CP] Copy Pods Resources";
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-PNObject_Tests/Pods-PNObject_Tests-resources.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
3FF38AA7E586B22171D1A118 /* [CP] Check Pods Manifest.lock */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
@ -346,46 +329,16 @@
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
"${SRCROOT}/Pods/Target Support Files/Pods-PNObject_Example/Pods-PNObject_Example-resources.sh",
|
||||
"${PODS_ROOT}/Target Support Files/Pods-PNObject_Example/Pods-PNObject_Example-resources.sh",
|
||||
"${PODS_ROOT}/FBSDKCoreKit/FacebookSDKStrings.bundle",
|
||||
);
|
||||
name = "[CP] Copy Pods Resources";
|
||||
outputPaths = (
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FacebookSDKStrings.bundle",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-PNObject_Example/Pods-PNObject_Example-resources.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
746A765B48FD98342716D81F /* [CP] Embed Pods Frameworks */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "[CP] Embed Pods Frameworks";
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-PNObject_Tests/Pods-PNObject_Tests-frameworks.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
C40CC8D7B1996A567BE82827 /* [CP] Embed Pods Frameworks */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "[CP] Embed Pods Frameworks";
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-PNObject_Example/Pods-PNObject_Example-frameworks.sh\"\n";
|
||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-PNObject_Example/Pods-PNObject_Example-resources.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
E4CE079F616C61EA34D08B8B /* [CP] Check Pods Manifest.lock */ = {
|
||||
@ -461,18 +414,27 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
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_DEPRECATED_OBJC_IMPLEMENTATIONS = 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_IMPLICIT_RETAIN_SELF = 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;
|
||||
@ -505,18 +467,27 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
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_DEPRECATED_OBJC_IMPLEMENTATIONS = 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_IMPLICIT_RETAIN_SELF = 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;
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0820"
|
||||
LastUpgradeVersion = "1120"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
@ -27,6 +27,15 @@
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "6003F589195388D20070C39A"
|
||||
BuildableName = "PNObject_Example.app"
|
||||
BlueprintName = "PNObject_Example"
|
||||
ReferencedContainer = "container:PNObject.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
@ -39,17 +48,6 @@
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "6003F589195388D20070C39A"
|
||||
BuildableName = "PNObject_Example.app"
|
||||
BlueprintName = "PNObject_Example"
|
||||
ReferencedContainer = "container:PNObject.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
@ -71,8 +69,6 @@
|
||||
ReferencedContainer = "container:PNObject.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
|
||||
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>IDEDidComputeMac32BitWarning</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
@ -1,152 +1,157 @@
|
||||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
},
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "Icon-40.png",
|
||||
"size" : "40x40",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "Icon-40@2x.png",
|
||||
"size" : "40x40",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "Icon-60@2x.png",
|
||||
"size" : "60x60",
|
||||
"size" : "20x20",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "NotificationIcon@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "Icon-72.png",
|
||||
"size" : "72x72",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "Icon-72@2x.png",
|
||||
"size" : "72x72",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "Icon-76.png",
|
||||
"size" : "76x76",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "Icon-76@2x.png",
|
||||
"size" : "76x76",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "Icon-Small-50.png",
|
||||
"size" : "50x50",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "Icon-Small-50@2x.png",
|
||||
"size" : "50x50",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x"
|
||||
"size" : "20x20",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "NotificationIcon@3x.png",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"size" : "29x29",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "Icon-Small.png",
|
||||
"size" : "29x29",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"size" : "29x29",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "Icon-Small@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "29x29",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "Icon.png",
|
||||
"size" : "57x57",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "Icon@2x.png",
|
||||
"size" : "57x57",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "Icon-Small@3x.png",
|
||||
"size" : "29x29",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"filename" : "Icon-40@3x.png",
|
||||
"size" : "40x40",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"filename" : "Icon-60@3x.png",
|
||||
"size" : "60x60",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"filename" : "Icon-40@2x.png",
|
||||
"size" : "40x40",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "Icon-Small.png",
|
||||
"size" : "29x29",
|
||||
"idiom" : "ipad",
|
||||
"size" : "40x40",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "Icon-40@3x.png",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"size" : "57x57",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "Icon.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "Icon-Small@2x.png",
|
||||
"size" : "29x29",
|
||||
"idiom" : "ipad",
|
||||
"size" : "57x57",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "Icon@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "60x60",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "Icon-60@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "60x60",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "Icon-60@3x.png",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"size" : "20x20",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "NotificationIcon~ipad.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"size" : "20x20",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "NotificationIcon~ipad@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "29x29",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "Icon-Small.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"size" : "29x29",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "Icon-Small@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "40x40",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "Icon-40.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"size" : "40x40",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "Icon-40@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "50x50",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "Icon-Small-50.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"size" : "50x50",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "Icon-Small-50@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "72x72",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "Icon-72.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"size" : "72x72",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "Icon-72@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "76x76",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "Icon-76.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"size" : "76x76",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "Icon-76@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "Icon-83.5@2x.png",
|
||||
"size" : "83.5x83.5",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "Icon-83.5@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "NotificationIcon@2x.png",
|
||||
"size" : "20x20",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "NotificationIcon@3x.png",
|
||||
"size" : "20x20",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"filename" : "NotificationIcon~ipad.png",
|
||||
"size" : "20x20",
|
||||
"idiom" : "ipad",
|
||||
"idiom" : "ios-marketing",
|
||||
"size" : "1024x1024",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "NotificationIcon~ipad@2x.png",
|
||||
"size" : "20x20",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x"
|
||||
}
|
||||
]
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
@ -25,7 +25,6 @@
|
||||
|
||||
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
|
||||
{
|
||||
|
||||
[FBSDKSettings setAppID:@"213761522305123"];
|
||||
|
||||
[[FBSDKApplicationDelegate sharedInstance] application:application didFinishLaunchingWithOptions:launchOptions];
|
||||
@ -34,21 +33,23 @@
|
||||
// Override point for customization after application launch.
|
||||
|
||||
[PNObjectConfig initSharedInstanceForEnvironments:@{EnvironmentDevelopment : @{BaseUrl:@"http://pnobject.local/",EndpointPath:@"api/v1/"},
|
||||
EnvironmentStage : @{BaseUrl:@"http://pnobject.stage.it/",EndpointPath:@"api/v1/"},
|
||||
EnvironmentStage : @{BaseUrl:@"https://pnobject.stage.it/",EndpointPath:@"api/v1/"},
|
||||
EnvironmentProduction : @{BaseUrl:@"http://pnobject.prod.it/",EndpointPath:@"api/v1/"},
|
||||
} userSubclass:[PNUser class] withOauthMode:OAuthModeClientCredential];
|
||||
} userSubclass:[PNUser class] andStoreClientIdentifier:@"ftiuyvkbyfi789ptf78"];
|
||||
|
||||
[[PNObjectConfig sharedInstance] setClientID:@"xxxxxxxxx" clientSecret:@"xxxxxxxxxxxx" forEnv:EnvironmentStage];
|
||||
[[PNObjectConfig sharedInstance] setClientID:@"xxxxxxxxx" clientSecret:@"xxxxxxxxxxxx" forEnv:EnvironmentProduction];
|
||||
/** Can user special char %@ to autoset EndpointPath to Oauth endpointPath */
|
||||
[[PNObjectConfig sharedInstance] setClientID:@"******" clientSecret:@"******" oAuthEndpointAction:@"%@oauth-token" oauthMode:OAuthModeClientCredential refreshTokenEnabled:NO forEnv:EnvironmentStage];
|
||||
[[PNObjectConfig sharedInstance] setClientID:@"******" clientSecret:@"******" oAuthEndpointAction:@"%@oauth-token" oauthMode:OAuthModePassword refreshTokenEnabled:NO forEnv:EnvironmentStage];
|
||||
|
||||
[[PNObjectConfig sharedInstance] setOauthUserName:@"admin" oauthPassword:@"admin" forEnv:EnvironmentStage];
|
||||
|
||||
[[PNObjectConfig sharedInstance] setEnvironment:EnvironmentStage];
|
||||
//[[PNObjectConfig sharedInstance] setHTTPHeaderValue:@"XMLHttpRequest" forKey:@"X-Request-With"];
|
||||
|
||||
NSLogDebug(@"%@",[[PNObjectConfig sharedInstance] baseUrl]);
|
||||
NSLogDebug(@"%@",[[PNObjectConfig sharedInstance] endPointPath]);
|
||||
NSLogDebug(@"%@",[[PNObjectConfig sharedInstance] endPointUrl]);
|
||||
|
||||
|
||||
[[PNObjectConfig sharedInstance] loadManagersWithCredentials];
|
||||
|
||||
PNObjViewController *viewController = [[PNObjViewController alloc] init];
|
||||
|
||||
|
||||
@ -93,11 +93,21 @@
|
||||
}
|
||||
|
||||
- (void) refreshTokenAction {
|
||||
[[PNObjectConfig sharedInstance] refreshTokenForClientCredential];
|
||||
[[PNObjectConfig sharedInstance] refreshTokenForClientCredentialWithBlockSuccess:^(BOOL refreshSuccess) {
|
||||
|
||||
[PNUser loginCurrentUserWithUsername:@"test" password:@"test" withBlockSuccess:^(PNUser * _Nullable responseObject) {
|
||||
|
||||
NSLog(@"success");
|
||||
} failure:^(NSError * _Nonnull error) {
|
||||
NSLog(@"%@",error);
|
||||
}];
|
||||
} failure:^(NSError * _Nonnull error) {
|
||||
|
||||
}];
|
||||
}
|
||||
|
||||
- (void) cancelTokenAction {
|
||||
[[PNObjectConfig sharedInstance] resetToken];
|
||||
[[PNObjectConfig sharedInstance] resetAllTokens];
|
||||
}
|
||||
|
||||
- (void) apiCallAction {
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
platform :ios, '9.0'
|
||||
platform :ios, '10.0'
|
||||
|
||||
target 'PNObject_Example' do
|
||||
pod 'PNObject', :path => '../'
|
||||
pod 'PureLayout'
|
||||
pod 'PNObject', :path => '../'
|
||||
pod 'PureLayout'
|
||||
|
||||
target 'PNObject_Tests' do
|
||||
target 'PNObject_Tests' do
|
||||
inherit! :search_paths
|
||||
|
||||
pod 'Specta'
|
||||
pod 'Expecta'
|
||||
pod 'Expecta'
|
||||
end
|
||||
end
|
||||
|
||||
@ -1,51 +1,51 @@
|
||||
PODS:
|
||||
- AFNetworking (3.1.0):
|
||||
- AFNetworking/NSURLSession (= 3.1.0)
|
||||
- AFNetworking/Reachability (= 3.1.0)
|
||||
- AFNetworking/Security (= 3.1.0)
|
||||
- AFNetworking/Serialization (= 3.1.0)
|
||||
- AFNetworking/UIKit (= 3.1.0)
|
||||
- AFNetworking/NSURLSession (3.1.0):
|
||||
- AFNetworking (4.0.1):
|
||||
- AFNetworking/NSURLSession (= 4.0.1)
|
||||
- AFNetworking/Reachability (= 4.0.1)
|
||||
- AFNetworking/Security (= 4.0.1)
|
||||
- AFNetworking/Serialization (= 4.0.1)
|
||||
- AFNetworking/UIKit (= 4.0.1)
|
||||
- AFNetworking/NSURLSession (4.0.1):
|
||||
- AFNetworking/Reachability
|
||||
- AFNetworking/Security
|
||||
- AFNetworking/Serialization
|
||||
- AFNetworking/Reachability (3.1.0)
|
||||
- AFNetworking/Security (3.1.0)
|
||||
- AFNetworking/Serialization (3.1.0)
|
||||
- AFNetworking/UIKit (3.1.0):
|
||||
- AFNetworking/Reachability (4.0.1)
|
||||
- AFNetworking/Security (4.0.1)
|
||||
- AFNetworking/Serialization (4.0.1)
|
||||
- AFNetworking/UIKit (4.0.1):
|
||||
- AFNetworking/NSURLSession
|
||||
- Bolts (1.9.0):
|
||||
- Bolts/AppLinks (= 1.9.0)
|
||||
- Bolts/Tasks (= 1.9.0)
|
||||
- Bolts/AppLinks (1.9.0):
|
||||
- Bolts/Tasks
|
||||
- Bolts/Tasks (1.9.0)
|
||||
- CodFis-Helper (0.1.3)
|
||||
- DDDKeychainWrapper (1.0.0)
|
||||
- DJLocalization (1.2.2):
|
||||
- DJLocalization/Core (= 1.2.2)
|
||||
- DJLocalization/Core (1.2.2)
|
||||
- Expecta (1.0.6)
|
||||
- FBSDKCoreKit (4.29.0):
|
||||
- Bolts (~> 1.7)
|
||||
- FBSDKLoginKit (4.29.0):
|
||||
- FBSDKCoreKit
|
||||
- FBSDKShareKit (4.29.0):
|
||||
- FBSDKCoreKit
|
||||
- FBSDKCoreKit (5.8.0):
|
||||
- FBSDKCoreKit/Basics (= 5.8.0)
|
||||
- FBSDKCoreKit/Core (= 5.8.0)
|
||||
- FBSDKCoreKit/Basics (5.8.0)
|
||||
- FBSDKCoreKit/Core (5.8.0):
|
||||
- FBSDKCoreKit/Basics
|
||||
- FBSDKLoginKit (5.8.0):
|
||||
- FBSDKLoginKit/Login (= 5.8.0)
|
||||
- FBSDKLoginKit/Login (5.8.0):
|
||||
- FBSDKCoreKit (~> 5.0)
|
||||
- FBSDKPlacesKit (5.15.0):
|
||||
- FBSDKCoreKit (~> 5.0)
|
||||
- FBSDKShareKit (5.8.0):
|
||||
- FBSDKShareKit/Share (= 5.8.0)
|
||||
- FBSDKShareKit/Share (5.8.0):
|
||||
- FBSDKCoreKit (~> 5.0)
|
||||
- NSDataAES (0.2.2)
|
||||
- NSDate_Utils (1.1.0):
|
||||
- DJLocalization
|
||||
- NSString-Helper (1.0.5)
|
||||
- NSDate_Utils (1.2.0)
|
||||
- NSString-Helper (1.2.0)
|
||||
- nv-ios-http-status (0.0.1)
|
||||
- PEAR-FileManager-iOS (1.3.1)
|
||||
- PNObject (1.3.4):
|
||||
- PNObject (2.7.0):
|
||||
- AFNetworking
|
||||
- CodFis-Helper
|
||||
- DDDKeychainWrapper
|
||||
- DJLocalization
|
||||
- FBSDKCoreKit
|
||||
- FBSDKLoginKit
|
||||
- FBSDKShareKit
|
||||
- FBSDKCoreKit (~> 5.8.0)
|
||||
- FBSDKLoginKit (~> 5.8.0)
|
||||
- FBSDKPlacesKit
|
||||
- FBSDKShareKit (~> 5.8.0)
|
||||
- NSDataAES
|
||||
- NSDate_Utils
|
||||
- NSString-Helper
|
||||
@ -54,11 +54,11 @@ PODS:
|
||||
- RZDataBinding
|
||||
- StrongestPasswordValidator
|
||||
- UIDevice-Utils
|
||||
- PureLayout (3.0.2)
|
||||
- RZDataBinding (2.1.0)
|
||||
- PureLayout (3.1.6)
|
||||
- RZDataBinding (2.1.1)
|
||||
- Specta (1.0.7)
|
||||
- StrongestPasswordValidator (0.1.2)
|
||||
- UIDevice-Utils (1.0.0)
|
||||
- UIDevice-Utils (1.1.0)
|
||||
|
||||
DEPENDENCIES:
|
||||
- Expecta
|
||||
@ -66,32 +66,52 @@ DEPENDENCIES:
|
||||
- PureLayout
|
||||
- Specta
|
||||
|
||||
SPEC REPOS:
|
||||
trunk:
|
||||
- AFNetworking
|
||||
- CodFis-Helper
|
||||
- DDDKeychainWrapper
|
||||
- Expecta
|
||||
- FBSDKCoreKit
|
||||
- FBSDKLoginKit
|
||||
- FBSDKPlacesKit
|
||||
- FBSDKShareKit
|
||||
- NSDataAES
|
||||
- NSDate_Utils
|
||||
- NSString-Helper
|
||||
- nv-ios-http-status
|
||||
- PEAR-FileManager-iOS
|
||||
- PureLayout
|
||||
- RZDataBinding
|
||||
- Specta
|
||||
- StrongestPasswordValidator
|
||||
- UIDevice-Utils
|
||||
|
||||
EXTERNAL SOURCES:
|
||||
PNObject:
|
||||
:path: ../
|
||||
:path: "../"
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
AFNetworking: 5e0e199f73d8626b11e79750991f5d173d1f8b67
|
||||
Bolts: ac6567323eac61e203f6a9763667d0f711be34c8
|
||||
AFNetworking: 7864c38297c79aaca1500c33288e429c3451fdce
|
||||
CodFis-Helper: 28be4c74d7202542459d72354f59b1215871de87
|
||||
DDDKeychainWrapper: e681a4daba6448786fa83b4941f58102a33b1897
|
||||
DJLocalization: 0c84029af375647d4104a42ae36be87194c46c47
|
||||
Expecta: 3b6bd90a64b9a1dcb0b70aa0e10a7f8f631667d5
|
||||
FBSDKCoreKit: 6f139173dc63a1deaff4430a55f2fe5bb222d2af
|
||||
FBSDKLoginKit: 56a057ca6822535ea0faa25f57a7c41edb697fd4
|
||||
FBSDKShareKit: 18a2cd118aef11dd657fe7e8b64bae5c719088b2
|
||||
FBSDKCoreKit: e7dcac0aabcfb09d0166998edd95fe3b05a0ce5d
|
||||
FBSDKLoginKit: 1b0cf04df0370b37404213157b060d6666ede814
|
||||
FBSDKPlacesKit: 26d4033cb8cc8640f1e0eadb3635e7dd29c4d073
|
||||
FBSDKShareKit: abc29e1bb4f9f59f679adf1ae191006d68455f96
|
||||
NSDataAES: 967ea3337476a80e9838a533c25d570a06855ed0
|
||||
NSDate_Utils: c858a89da6e204ecf53aca48dbccb4da4d25bc9e
|
||||
NSString-Helper: 459e1b6a62b3bf7db10f01b0d102548608e945c4
|
||||
NSDate_Utils: 6eb35593eef7faed4ca360e00f889c541eb11f4a
|
||||
NSString-Helper: 1c259caa6c845e79e0bb45ee25e34f95d86d2317
|
||||
nv-ios-http-status: b6c2b5fc8656cc19e0d3000dadce2080b99d0e2f
|
||||
PEAR-FileManager-iOS: 3bc403f68a53483f5629aa822f4649e40275c4d3
|
||||
PNObject: 8dc560680a7a48060ed309b019ca4fc41505cc07
|
||||
PureLayout: 4d550abe49a94f24c2808b9b95db9131685fe4cd
|
||||
RZDataBinding: 6981e90ddaae2f5e02028323b1043f8c31013109
|
||||
PNObject: 7571fadf9beea35be49984210f8717a889b7be2b
|
||||
PureLayout: bd3c4ec3a3819ad387c99ebb72c6b129c3ed4d2d
|
||||
RZDataBinding: 289e2fbdce8b9585afef69def83425c5d380ffbd
|
||||
Specta: 3e1bd89c3517421982dc4d1c992503e48bd5fe66
|
||||
StrongestPasswordValidator: 921e42615bdf353513c6f925bffd4fc29865dbd7
|
||||
UIDevice-Utils: ff37bd042127117572d6ce4c5ff074f4f54ab5ed
|
||||
UIDevice-Utils: 9efdf0f9fb3c07a53595d39981d3c04ce39aba5f
|
||||
|
||||
PODFILE CHECKSUM: fcd5d1cf3426c7c9c5b3e5edcd4b8e5402ee7f2e
|
||||
PODFILE CHECKSUM: 1dc1681e732f85afd3765f49346295b2d8460102
|
||||
|
||||
COCOAPODS: 1.3.1
|
||||
COCOAPODS: 1.10.0
|
||||
|
||||
@ -1,8 +1,5 @@
|
||||
//
|
||||
// UIImage+AFNetworking.h
|
||||
//
|
||||
//
|
||||
// Created by Paulo Ferreira on 08/07/15.
|
||||
// AFCompatibilityMacros.h
|
||||
// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ )
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
@ -22,14 +19,31 @@
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
#if TARGET_OS_IOS || TARGET_OS_TV
|
||||
#ifndef AFCompatibilityMacros_h
|
||||
#define AFCompatibilityMacros_h
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#ifdef API_AVAILABLE
|
||||
#define AF_API_AVAILABLE(...) API_AVAILABLE(__VA_ARGS__)
|
||||
#else
|
||||
#define AF_API_AVAILABLE(...)
|
||||
#endif // API_AVAILABLE
|
||||
|
||||
@interface UIImage (AFNetworking)
|
||||
|
||||
+ (UIImage*) safeImageWithData:(NSData*)data;
|
||||
|
||||
@end
|
||||
#ifdef API_UNAVAILABLE
|
||||
#define AF_API_UNAVAILABLE(...) API_UNAVAILABLE(__VA_ARGS__)
|
||||
#else
|
||||
#define AF_API_UNAVAILABLE(...)
|
||||
#endif // API_UNAVAILABLE
|
||||
|
||||
#if __has_warning("-Wunguarded-availability-new")
|
||||
#define AF_CAN_USE_AT_AVAILABLE 1
|
||||
#else
|
||||
#define AF_CAN_USE_AT_AVAILABLE 0
|
||||
#endif
|
||||
|
||||
#if ((__IPHONE_OS_VERSION_MAX_ALLOWED && __IPHONE_OS_VERSION_MAX_ALLOWED < 100000) || (__MAC_OS_VERSION_MAX_ALLOWED && __MAC_OS_VERSION_MAX_ALLOWED < 101200) ||(__WATCH_OS_MAX_VERSION_ALLOWED && __WATCH_OS_MAX_VERSION_ALLOWED < 30000) ||(__TV_OS_MAX_VERSION_ALLOWED && __TV_OS_MAX_VERSION_ALLOWED < 100000))
|
||||
#define AF_CAN_INCLUDE_SESSION_TASK_METRICS 0
|
||||
#else
|
||||
#define AF_CAN_INCLUDE_SESSION_TASK_METRICS 1
|
||||
#endif
|
||||
|
||||
#endif /* AFCompatibilityMacros_h */
|
||||
@ -25,12 +25,6 @@
|
||||
#endif
|
||||
#import <TargetConditionals.h>
|
||||
|
||||
#if TARGET_OS_IOS || TARGET_OS_WATCH || TARGET_OS_TV
|
||||
#import <MobileCoreServices/MobileCoreServices.h>
|
||||
#else
|
||||
#import <CoreServices/CoreServices.h>
|
||||
#endif
|
||||
|
||||
#import "AFURLSessionManager.h"
|
||||
|
||||
/**
|
||||
@ -40,11 +34,9 @@
|
||||
|
||||
Developers targeting iOS 7 or Mac OS X 10.9 or later that deal extensively with a web service are encouraged to subclass `AFHTTPSessionManager`, providing a class method that returns a shared singleton object on which authentication and other configuration can be shared across the application.
|
||||
|
||||
For developers targeting iOS 6 or Mac OS X 10.8 or earlier, `AFHTTPRequestOperationManager` may be used to similar effect.
|
||||
|
||||
## Methods to Override
|
||||
|
||||
To change the behavior of all data task operation construction, which is also used in the `GET` / `POST` / et al. convenience methods, override `dataTaskWithRequest:completionHandler:`.
|
||||
To change the behavior of all data task operation construction, which is also used in the `GET` / `POST` / et al. convenience methods, override `dataTaskWithRequest:uploadProgress:downloadProgress:completionHandler:`.
|
||||
|
||||
## Serialization
|
||||
|
||||
@ -94,6 +86,15 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
*/
|
||||
@property (nonatomic, strong) AFHTTPResponseSerializer <AFURLResponseSerialization> * responseSerializer;
|
||||
|
||||
///-------------------------------
|
||||
/// @name Managing Security Policy
|
||||
///-------------------------------
|
||||
|
||||
/**
|
||||
The security policy used by created session to evaluate server trust for secure connections. `AFURLSessionManager` uses the `defaultPolicy` unless otherwise specified. A security policy configured with `AFSSLPinningModePublicKey` or `AFSSLPinningModeCertificate` can only be applied on a session manager initialized with a secure base URL (i.e. https). Applying a security policy with pinning enabled on an insecure session manager throws an `Invalid Security Policy` exception.
|
||||
*/
|
||||
@property (nonatomic, strong) AFSecurityPolicy *securityPolicy;
|
||||
|
||||
///---------------------
|
||||
/// @name Initialization
|
||||
///---------------------
|
||||
@ -131,115 +132,75 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
Creates and runs an `NSURLSessionDataTask` with a `GET` request.
|
||||
|
||||
@param URLString The URL string used to create the request URL.
|
||||
@param parameters The parameters to be encoded according to the client request serializer.
|
||||
@param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer.
|
||||
@param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred.
|
||||
|
||||
@see -dataTaskWithRequest:completionHandler:
|
||||
*/
|
||||
- (nullable NSURLSessionDataTask *)GET:(NSString *)URLString
|
||||
parameters:(nullable id)parameters
|
||||
success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
|
||||
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure DEPRECATED_ATTRIBUTE;
|
||||
|
||||
|
||||
/**
|
||||
Creates and runs an `NSURLSessionDataTask` with a `GET` request.
|
||||
|
||||
|
||||
@param URLString The URL string used to create the request URL.
|
||||
@param parameters The parameters to be encoded according to the client request serializer.
|
||||
@param headers The headers appended to the default headers for this request.
|
||||
@param downloadProgress A block object to be executed when the download progress is updated. Note this block is called on the session queue, not the main queue.
|
||||
@param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer.
|
||||
@param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred.
|
||||
|
||||
|
||||
@see -dataTaskWithRequest:uploadProgress:downloadProgress:completionHandler:
|
||||
*/
|
||||
- (nullable NSURLSessionDataTask *)GET:(NSString *)URLString
|
||||
parameters:(nullable id)parameters
|
||||
headers:(nullable NSDictionary <NSString *, NSString *> *)headers
|
||||
progress:(nullable void (^)(NSProgress *downloadProgress))downloadProgress
|
||||
success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
|
||||
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
|
||||
|
||||
/**
|
||||
Creates and runs an `NSURLSessionDataTask` with a `HEAD` request.
|
||||
|
||||
|
||||
@param URLString The URL string used to create the request URL.
|
||||
@param parameters The parameters to be encoded according to the client request serializer.
|
||||
@param headers The headers appended to the default headers for this request.
|
||||
@param success A block object to be executed when the task finishes successfully. This block has no return value and takes a single arguments: the data task.
|
||||
@param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred.
|
||||
|
||||
|
||||
@see -dataTaskWithRequest:completionHandler:
|
||||
*/
|
||||
- (nullable NSURLSessionDataTask *)HEAD:(NSString *)URLString
|
||||
parameters:(nullable id)parameters
|
||||
success:(nullable void (^)(NSURLSessionDataTask *task))success
|
||||
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
|
||||
parameters:(nullable id)parameters
|
||||
headers:(nullable NSDictionary <NSString *, NSString *> *)headers
|
||||
success:(nullable void (^)(NSURLSessionDataTask *task))success
|
||||
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
|
||||
|
||||
/**
|
||||
Creates and runs an `NSURLSessionDataTask` with a `POST` request.
|
||||
|
||||
@param URLString The URL string used to create the request URL.
|
||||
@param parameters The parameters to be encoded according to the client request serializer.
|
||||
@param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer.
|
||||
@param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred.
|
||||
|
||||
@see -dataTaskWithRequest:completionHandler:
|
||||
*/
|
||||
- (nullable NSURLSessionDataTask *)POST:(NSString *)URLString
|
||||
parameters:(nullable id)parameters
|
||||
success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
|
||||
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure DEPRECATED_ATTRIBUTE;
|
||||
|
||||
/**
|
||||
Creates and runs an `NSURLSessionDataTask` with a `POST` request.
|
||||
|
||||
|
||||
@param URLString The URL string used to create the request URL.
|
||||
@param parameters The parameters to be encoded according to the client request serializer.
|
||||
@param headers The headers appended to the default headers for this request.
|
||||
@param uploadProgress A block object to be executed when the upload progress is updated. Note this block is called on the session queue, not the main queue.
|
||||
@param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer.
|
||||
@param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred.
|
||||
|
||||
|
||||
@see -dataTaskWithRequest:uploadProgress:downloadProgress:completionHandler:
|
||||
*/
|
||||
- (nullable NSURLSessionDataTask *)POST:(NSString *)URLString
|
||||
parameters:(nullable id)parameters
|
||||
headers:(nullable NSDictionary <NSString *, NSString *> *)headers
|
||||
progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgress
|
||||
success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
|
||||
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
|
||||
|
||||
/**
|
||||
Creates and runs an `NSURLSessionDataTask` with a multipart `POST` request.
|
||||
|
||||
@param URLString The URL string used to create the request URL.
|
||||
@param parameters The parameters to be encoded according to the client request serializer.
|
||||
@param block A block that takes a single argument and appends data to the HTTP body. The block argument is an object adopting the `AFMultipartFormData` protocol.
|
||||
@param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer.
|
||||
@param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred.
|
||||
|
||||
@see -dataTaskWithRequest:completionHandler:
|
||||
*/
|
||||
- (nullable NSURLSessionDataTask *)POST:(NSString *)URLString
|
||||
parameters:(nullable id)parameters
|
||||
constructingBodyWithBlock:(nullable void (^)(id <AFMultipartFormData> formData))block
|
||||
success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
|
||||
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure DEPRECATED_ATTRIBUTE;
|
||||
|
||||
/**
|
||||
Creates and runs an `NSURLSessionDataTask` with a multipart `POST` request.
|
||||
|
||||
|
||||
@param URLString The URL string used to create the request URL.
|
||||
@param parameters The parameters to be encoded according to the client request serializer.
|
||||
@param headers The headers appended to the default headers for this request.
|
||||
@param block A block that takes a single argument and appends data to the HTTP body. The block argument is an object adopting the `AFMultipartFormData` protocol.
|
||||
@param uploadProgress A block object to be executed when the upload progress is updated. Note this block is called on the session queue, not the main queue.
|
||||
@param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer.
|
||||
@param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred.
|
||||
|
||||
|
||||
@see -dataTaskWithRequest:uploadProgress:downloadProgress:completionHandler:
|
||||
*/
|
||||
- (nullable NSURLSessionDataTask *)POST:(NSString *)URLString
|
||||
parameters:(nullable id)parameters
|
||||
headers:(nullable NSDictionary <NSString *, NSString *> *)headers
|
||||
constructingBodyWithBlock:(nullable void (^)(id <AFMultipartFormData> formData))block
|
||||
progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgress
|
||||
success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
|
||||
@ -247,48 +208,77 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
Creates and runs an `NSURLSessionDataTask` with a `PUT` request.
|
||||
|
||||
|
||||
@param URLString The URL string used to create the request URL.
|
||||
@param parameters The parameters to be encoded according to the client request serializer.
|
||||
@param headers The headers appended to the default headers for this request.
|
||||
@param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer.
|
||||
@param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred.
|
||||
|
||||
|
||||
@see -dataTaskWithRequest:completionHandler:
|
||||
*/
|
||||
- (nullable NSURLSessionDataTask *)PUT:(NSString *)URLString
|
||||
parameters:(nullable id)parameters
|
||||
success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
|
||||
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
|
||||
parameters:(nullable id)parameters
|
||||
headers:(nullable NSDictionary <NSString *, NSString *> *)headers
|
||||
success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
|
||||
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
|
||||
|
||||
/**
|
||||
Creates and runs an `NSURLSessionDataTask` with a `PATCH` request.
|
||||
|
||||
|
||||
@param URLString The URL string used to create the request URL.
|
||||
@param parameters The parameters to be encoded according to the client request serializer.
|
||||
@param headers The headers appended to the default headers for this request.
|
||||
@param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer.
|
||||
@param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred.
|
||||
|
||||
|
||||
@see -dataTaskWithRequest:completionHandler:
|
||||
*/
|
||||
- (nullable NSURLSessionDataTask *)PATCH:(NSString *)URLString
|
||||
parameters:(nullable id)parameters
|
||||
success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
|
||||
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
|
||||
parameters:(nullable id)parameters
|
||||
headers:(nullable NSDictionary <NSString *, NSString *> *)headers
|
||||
success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
|
||||
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
|
||||
|
||||
/**
|
||||
Creates and runs an `NSURLSessionDataTask` with a `DELETE` request.
|
||||
|
||||
|
||||
@param URLString The URL string used to create the request URL.
|
||||
@param parameters The parameters to be encoded according to the client request serializer.
|
||||
@param headers The headers appended to the default headers for this request.
|
||||
@param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer.
|
||||
@param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred.
|
||||
|
||||
|
||||
@see -dataTaskWithRequest:completionHandler:
|
||||
*/
|
||||
- (nullable NSURLSessionDataTask *)DELETE:(NSString *)URLString
|
||||
parameters:(nullable id)parameters
|
||||
success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
|
||||
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
|
||||
parameters:(nullable id)parameters
|
||||
headers:(nullable NSDictionary <NSString *, NSString *> *)headers
|
||||
success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
|
||||
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
|
||||
|
||||
/**
|
||||
Creates an `NSURLSessionDataTask` with a custom `HTTPMethod` request.
|
||||
|
||||
@param method The HTTPMethod string used to create the request.
|
||||
@param URLString The URL string used to create the request URL.
|
||||
@param parameters The parameters to be encoded according to the client request serializer.
|
||||
@param headers The headers appended to the default headers for this request.
|
||||
@param uploadProgress A block object to be executed when the upload progress is updated. Note this block is called on the session queue, not the main queue.
|
||||
@param downloadProgress A block object to be executed when the download progress is updated. Note this block is called on the session queue, not the main queue.
|
||||
@param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer.
|
||||
@param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred.
|
||||
|
||||
@see -dataTaskWithRequest:uploadProgress:downloadProgress:completionHandler:
|
||||
*/
|
||||
- (nullable NSURLSessionDataTask *)dataTaskWithHTTPMethod:(NSString *)method
|
||||
URLString:(NSString *)URLString
|
||||
parameters:(nullable id)parameters
|
||||
headers:(nullable NSDictionary <NSString *, NSString *> *)headers
|
||||
uploadProgress:(nullable void (^)(NSProgress *uploadProgress))uploadProgress
|
||||
downloadProgress:(nullable void (^)(NSProgress *downloadProgress))downloadProgress
|
||||
success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
|
||||
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@ -98,105 +98,100 @@
|
||||
[super setResponseSerializer:responseSerializer];
|
||||
}
|
||||
|
||||
@dynamic securityPolicy;
|
||||
|
||||
- (void)setSecurityPolicy:(AFSecurityPolicy *)securityPolicy {
|
||||
if (securityPolicy.SSLPinningMode != AFSSLPinningModeNone && ![self.baseURL.scheme isEqualToString:@"https"]) {
|
||||
NSString *pinningMode = @"Unknown Pinning Mode";
|
||||
switch (securityPolicy.SSLPinningMode) {
|
||||
case AFSSLPinningModeNone: pinningMode = @"AFSSLPinningModeNone"; break;
|
||||
case AFSSLPinningModeCertificate: pinningMode = @"AFSSLPinningModeCertificate"; break;
|
||||
case AFSSLPinningModePublicKey: pinningMode = @"AFSSLPinningModePublicKey"; break;
|
||||
}
|
||||
NSString *reason = [NSString stringWithFormat:@"A security policy configured with `%@` can only be applied on a manager with a secure base URL (i.e. https)", pinningMode];
|
||||
@throw [NSException exceptionWithName:@"Invalid Security Policy" reason:reason userInfo:nil];
|
||||
}
|
||||
|
||||
[super setSecurityPolicy:securityPolicy];
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
- (NSURLSessionDataTask *)GET:(NSString *)URLString
|
||||
parameters:(id)parameters
|
||||
success:(void (^)(NSURLSessionDataTask *task, id responseObject))success
|
||||
failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure
|
||||
parameters:(nullable id)parameters
|
||||
headers:(nullable NSDictionary <NSString *, NSString *> *)headers
|
||||
progress:(nullable void (^)(NSProgress * _Nonnull))downloadProgress
|
||||
success:(nullable void (^)(NSURLSessionDataTask * _Nonnull, id _Nullable))success
|
||||
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable, NSError * _Nonnull))failure
|
||||
{
|
||||
|
||||
return [self GET:URLString parameters:parameters progress:nil success:success failure:failure];
|
||||
}
|
||||
|
||||
- (NSURLSessionDataTask *)GET:(NSString *)URLString
|
||||
parameters:(id)parameters
|
||||
progress:(void (^)(NSProgress * _Nonnull))downloadProgress
|
||||
success:(void (^)(NSURLSessionDataTask * _Nonnull, id _Nullable))success
|
||||
failure:(void (^)(NSURLSessionDataTask * _Nullable, NSError * _Nonnull))failure
|
||||
{
|
||||
|
||||
|
||||
NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"GET"
|
||||
URLString:URLString
|
||||
parameters:parameters
|
||||
headers:headers
|
||||
uploadProgress:nil
|
||||
downloadProgress:downloadProgress
|
||||
success:success
|
||||
failure:failure];
|
||||
|
||||
|
||||
[dataTask resume];
|
||||
|
||||
|
||||
return dataTask;
|
||||
}
|
||||
|
||||
- (NSURLSessionDataTask *)HEAD:(NSString *)URLString
|
||||
parameters:(id)parameters
|
||||
success:(void (^)(NSURLSessionDataTask *task))success
|
||||
failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure
|
||||
parameters:(nullable id)parameters
|
||||
headers:(nullable NSDictionary<NSString *,NSString *> *)headers
|
||||
success:(nullable void (^)(NSURLSessionDataTask * _Nonnull))success
|
||||
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable, NSError * _Nonnull))failure
|
||||
{
|
||||
NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"HEAD" URLString:URLString parameters:parameters uploadProgress:nil downloadProgress:nil success:^(NSURLSessionDataTask *task, __unused id responseObject) {
|
||||
NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"HEAD" URLString:URLString parameters:parameters headers:headers uploadProgress:nil downloadProgress:nil success:^(NSURLSessionDataTask *task, __unused id responseObject) {
|
||||
if (success) {
|
||||
success(task);
|
||||
}
|
||||
} failure:failure];
|
||||
|
||||
|
||||
[dataTask resume];
|
||||
|
||||
|
||||
return dataTask;
|
||||
}
|
||||
|
||||
- (NSURLSessionDataTask *)POST:(NSString *)URLString
|
||||
parameters:(id)parameters
|
||||
success:(void (^)(NSURLSessionDataTask *task, id responseObject))success
|
||||
failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure
|
||||
- (nullable NSURLSessionDataTask *)POST:(NSString *)URLString
|
||||
parameters:(nullable id)parameters
|
||||
headers:(nullable NSDictionary <NSString *, NSString *> *)headers
|
||||
progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgress
|
||||
success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
|
||||
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure
|
||||
{
|
||||
return [self POST:URLString parameters:parameters progress:nil success:success failure:failure];
|
||||
}
|
||||
|
||||
- (NSURLSessionDataTask *)POST:(NSString *)URLString
|
||||
parameters:(id)parameters
|
||||
progress:(void (^)(NSProgress * _Nonnull))uploadProgress
|
||||
success:(void (^)(NSURLSessionDataTask * _Nonnull, id _Nullable))success
|
||||
failure:(void (^)(NSURLSessionDataTask * _Nullable, NSError * _Nonnull))failure
|
||||
{
|
||||
NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"POST" URLString:URLString parameters:parameters uploadProgress:uploadProgress downloadProgress:nil success:success failure:failure];
|
||||
|
||||
NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"POST" URLString:URLString parameters:parameters headers:headers uploadProgress:uploadProgress downloadProgress:nil success:success failure:failure];
|
||||
|
||||
[dataTask resume];
|
||||
|
||||
|
||||
return dataTask;
|
||||
}
|
||||
|
||||
- (NSURLSessionDataTask *)POST:(NSString *)URLString
|
||||
parameters:(nullable id)parameters
|
||||
headers:(nullable NSDictionary<NSString *,NSString *> *)headers
|
||||
constructingBodyWithBlock:(nullable void (^)(id<AFMultipartFormData> _Nonnull))block
|
||||
success:(nullable void (^)(NSURLSessionDataTask * _Nonnull, id _Nullable))success
|
||||
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable, NSError * _Nonnull))failure
|
||||
{
|
||||
return [self POST:URLString parameters:parameters constructingBodyWithBlock:block progress:nil success:success failure:failure];
|
||||
}
|
||||
|
||||
- (NSURLSessionDataTask *)POST:(NSString *)URLString
|
||||
parameters:(id)parameters
|
||||
constructingBodyWithBlock:(void (^)(id <AFMultipartFormData> formData))block
|
||||
progress:(nullable void (^)(NSProgress * _Nonnull))uploadProgress
|
||||
success:(void (^)(NSURLSessionDataTask *task, id responseObject))success
|
||||
failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure
|
||||
success:(nullable void (^)(NSURLSessionDataTask * _Nonnull, id _Nullable))success failure:(void (^)(NSURLSessionDataTask * _Nullable, NSError * _Nonnull))failure
|
||||
{
|
||||
NSError *serializationError = nil;
|
||||
NSMutableURLRequest *request = [self.requestSerializer multipartFormRequestWithMethod:@"POST" URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters constructingBodyWithBlock:block error:&serializationError];
|
||||
for (NSString *headerField in headers.keyEnumerator) {
|
||||
[request setValue:headers[headerField] forHTTPHeaderField:headerField];
|
||||
}
|
||||
if (serializationError) {
|
||||
if (failure) {
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wgnu"
|
||||
dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{
|
||||
failure(nil, serializationError);
|
||||
});
|
||||
#pragma clang diagnostic pop
|
||||
}
|
||||
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
|
||||
__block NSURLSessionDataTask *task = [self uploadTaskWithStreamedRequest:request progress:uploadProgress completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) {
|
||||
if (error) {
|
||||
if (failure) {
|
||||
@ -208,66 +203,71 @@
|
||||
}
|
||||
}
|
||||
}];
|
||||
|
||||
|
||||
[task resume];
|
||||
|
||||
|
||||
return task;
|
||||
}
|
||||
|
||||
- (NSURLSessionDataTask *)PUT:(NSString *)URLString
|
||||
parameters:(id)parameters
|
||||
success:(void (^)(NSURLSessionDataTask *task, id responseObject))success
|
||||
failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure
|
||||
parameters:(nullable id)parameters
|
||||
headers:(nullable NSDictionary<NSString *,NSString *> *)headers
|
||||
success:(nullable void (^)(NSURLSessionDataTask *task, id responseObject))success
|
||||
failure:(nullable void (^)(NSURLSessionDataTask *task, NSError *error))failure
|
||||
{
|
||||
NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"PUT" URLString:URLString parameters:parameters uploadProgress:nil downloadProgress:nil success:success failure:failure];
|
||||
|
||||
NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"PUT" URLString:URLString parameters:parameters headers:headers uploadProgress:nil downloadProgress:nil success:success failure:failure];
|
||||
|
||||
[dataTask resume];
|
||||
|
||||
|
||||
return dataTask;
|
||||
}
|
||||
|
||||
- (NSURLSessionDataTask *)PATCH:(NSString *)URLString
|
||||
parameters:(id)parameters
|
||||
success:(void (^)(NSURLSessionDataTask *task, id responseObject))success
|
||||
failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure
|
||||
parameters:(nullable id)parameters
|
||||
headers:(nullable NSDictionary<NSString *,NSString *> *)headers
|
||||
success:(nullable void (^)(NSURLSessionDataTask *task, id responseObject))success
|
||||
failure:(nullable void (^)(NSURLSessionDataTask *task, NSError *error))failure
|
||||
{
|
||||
NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"PATCH" URLString:URLString parameters:parameters uploadProgress:nil downloadProgress:nil success:success failure:failure];
|
||||
|
||||
NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"PATCH" URLString:URLString parameters:parameters headers:headers uploadProgress:nil downloadProgress:nil success:success failure:failure];
|
||||
|
||||
[dataTask resume];
|
||||
|
||||
|
||||
return dataTask;
|
||||
}
|
||||
|
||||
- (NSURLSessionDataTask *)DELETE:(NSString *)URLString
|
||||
parameters:(id)parameters
|
||||
success:(void (^)(NSURLSessionDataTask *task, id responseObject))success
|
||||
failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure
|
||||
parameters:(nullable id)parameters
|
||||
headers:(nullable NSDictionary<NSString *,NSString *> *)headers
|
||||
success:(nullable void (^)(NSURLSessionDataTask *task, id responseObject))success
|
||||
failure:(nullable void (^)(NSURLSessionDataTask *task, NSError *error))failure
|
||||
{
|
||||
NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"DELETE" URLString:URLString parameters:parameters uploadProgress:nil downloadProgress:nil success:success failure:failure];
|
||||
|
||||
NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"DELETE" URLString:URLString parameters:parameters headers:headers uploadProgress:nil downloadProgress:nil success:success failure:failure];
|
||||
|
||||
[dataTask resume];
|
||||
|
||||
|
||||
return dataTask;
|
||||
}
|
||||
|
||||
|
||||
- (NSURLSessionDataTask *)dataTaskWithHTTPMethod:(NSString *)method
|
||||
URLString:(NSString *)URLString
|
||||
parameters:(id)parameters
|
||||
parameters:(nullable id)parameters
|
||||
headers:(nullable NSDictionary <NSString *, NSString *> *)headers
|
||||
uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgress
|
||||
downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgress
|
||||
success:(void (^)(NSURLSessionDataTask *, id))success
|
||||
failure:(void (^)(NSURLSessionDataTask *, NSError *))failure
|
||||
success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
|
||||
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure
|
||||
{
|
||||
NSError *serializationError = nil;
|
||||
NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError];
|
||||
for (NSString *headerField in headers.keyEnumerator) {
|
||||
[request setValue:headers[headerField] forHTTPHeaderField:headerField];
|
||||
}
|
||||
if (serializationError) {
|
||||
if (failure) {
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wgnu"
|
||||
dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{
|
||||
failure(nil, serializationError);
|
||||
});
|
||||
#pragma clang diagnostic pop
|
||||
}
|
||||
|
||||
return nil;
|
||||
@ -310,11 +310,7 @@
|
||||
if (!configuration) {
|
||||
NSString *configurationIdentifier = [decoder decodeObjectOfClass:[NSString class] forKey:@"identifier"];
|
||||
if (configurationIdentifier) {
|
||||
#if (defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1100)
|
||||
configuration = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:configurationIdentifier];
|
||||
#else
|
||||
configuration = [NSURLSessionConfiguration backgroundSessionConfiguration:configurationIdentifier];
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -107,6 +107,16 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
*/
|
||||
- (instancetype)initWithReachability:(SCNetworkReachabilityRef)reachability NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
/**
|
||||
* Unavailable initializer
|
||||
*/
|
||||
+ (instancetype)new NS_UNAVAILABLE;
|
||||
|
||||
/**
|
||||
* Unavailable initializer
|
||||
*/
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
|
||||
///--------------------------------------------------
|
||||
/// @name Starting & Stopping Reachability Monitoring
|
||||
///--------------------------------------------------
|
||||
|
||||
@ -32,6 +32,7 @@ NSString * const AFNetworkingReachabilityDidChangeNotification = @"com.alamofire
|
||||
NSString * const AFNetworkingReachabilityNotificationStatusItem = @"AFNetworkingReachabilityNotificationStatusItem";
|
||||
|
||||
typedef void (^AFNetworkReachabilityStatusBlock)(AFNetworkReachabilityStatus status);
|
||||
typedef AFNetworkReachabilityManager * (^AFNetworkReachabilityStatusCallback)(AFNetworkReachabilityStatus status);
|
||||
|
||||
NSString * AFStringFromNetworkReachabilityStatus(AFNetworkReachabilityStatus status) {
|
||||
switch (status) {
|
||||
@ -78,20 +79,21 @@ static AFNetworkReachabilityStatus AFNetworkReachabilityStatusForFlags(SCNetwork
|
||||
* a queued notification (for an earlier status condition) is processed after
|
||||
* the later update, resulting in the listener being left in the wrong state.
|
||||
*/
|
||||
static void AFPostReachabilityStatusChange(SCNetworkReachabilityFlags flags, AFNetworkReachabilityStatusBlock block) {
|
||||
static void AFPostReachabilityStatusChange(SCNetworkReachabilityFlags flags, AFNetworkReachabilityStatusCallback block) {
|
||||
AFNetworkReachabilityStatus status = AFNetworkReachabilityStatusForFlags(flags);
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
AFNetworkReachabilityManager *manager = nil;
|
||||
if (block) {
|
||||
block(status);
|
||||
manager = block(status);
|
||||
}
|
||||
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
|
||||
NSDictionary *userInfo = @{ AFNetworkingReachabilityNotificationStatusItem: @(status) };
|
||||
[notificationCenter postNotificationName:AFNetworkingReachabilityDidChangeNotification object:nil userInfo:userInfo];
|
||||
[notificationCenter postNotificationName:AFNetworkingReachabilityDidChangeNotification object:manager userInfo:userInfo];
|
||||
});
|
||||
}
|
||||
|
||||
static void AFNetworkReachabilityCallback(SCNetworkReachabilityRef __unused target, SCNetworkReachabilityFlags flags, void *info) {
|
||||
AFPostReachabilityStatusChange(flags, (__bridge AFNetworkReachabilityStatusBlock)info);
|
||||
AFPostReachabilityStatusChange(flags, (__bridge AFNetworkReachabilityStatusCallback)info);
|
||||
}
|
||||
|
||||
|
||||
@ -170,8 +172,11 @@ static void AFNetworkReachabilityReleaseCallback(const void *info) {
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)init NS_UNAVAILABLE
|
||||
- (instancetype)init
|
||||
{
|
||||
@throw [NSException exceptionWithName:NSGenericException
|
||||
reason:@"`-init` unavailable. Use `-initWithReachability:` instead"
|
||||
userInfo:nil];
|
||||
return nil;
|
||||
}
|
||||
|
||||
@ -207,14 +212,15 @@ static void AFNetworkReachabilityReleaseCallback(const void *info) {
|
||||
}
|
||||
|
||||
__weak __typeof(self)weakSelf = self;
|
||||
AFNetworkReachabilityStatusBlock callback = ^(AFNetworkReachabilityStatus status) {
|
||||
AFNetworkReachabilityStatusCallback callback = ^(AFNetworkReachabilityStatus status) {
|
||||
__strong __typeof(weakSelf)strongSelf = weakSelf;
|
||||
|
||||
strongSelf.networkReachabilityStatus = status;
|
||||
if (strongSelf.networkReachabilityStatusBlock) {
|
||||
strongSelf.networkReachabilityStatusBlock(status);
|
||||
}
|
||||
|
||||
|
||||
return strongSelf;
|
||||
};
|
||||
|
||||
SCNetworkReachabilityContext context = {0, (__bridge void *)callback, AFNetworkReachabilityRetainCallback, AFNetworkReachabilityReleaseCallback, NULL};
|
||||
|
||||
@ -45,10 +45,10 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
The certificates used to evaluate server trust according to the SSL pinning mode.
|
||||
|
||||
By default, this property is set to any (`.cer`) certificates included in the target compiling AFNetworking. Note that if you are using AFNetworking as embedded framework, no certificates will be pinned by default. Use `certificatesInBundle` to load certificates from your target, and then create a new policy by calling `policyWithPinningMode:withPinnedCertificates`.
|
||||
|
||||
Note that if pinning is enabled, `evaluateServerTrust:forDomain:` will return true if any pinned certificate matches.
|
||||
|
||||
@see policyWithPinningMode:withPinnedCertificates:
|
||||
*/
|
||||
@property (nonatomic, strong, nullable) NSSet <NSData *> *pinnedCertificates;
|
||||
|
||||
@ -90,10 +90,14 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
Creates and returns a security policy with the specified pinning mode.
|
||||
|
||||
Certificates with the `.cer` extension found in the main bundle will be pinned. If you want more control over which certificates are pinned, please use `policyWithPinningMode:withPinnedCertificates:` instead.
|
||||
|
||||
@param pinningMode The SSL pinning mode.
|
||||
|
||||
@return A new security policy.
|
||||
|
||||
@see -policyWithPinningMode:withPinnedCertificates:
|
||||
*/
|
||||
+ (instancetype)policyWithPinningMode:(AFSSLPinningMode)pinningMode;
|
||||
|
||||
@ -104,7 +108,10 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
@param pinnedCertificates The certificates to pin against.
|
||||
|
||||
@return A new security policy.
|
||||
*/
|
||||
|
||||
@see +certificatesInBundle:
|
||||
@see -pinnedCertificates
|
||||
*/
|
||||
+ (instancetype)policyWithPinningMode:(AFSSLPinningMode)pinningMode withPinnedCertificates:(NSSet <NSData *> *)pinnedCertificates;
|
||||
|
||||
///------------------------------
|
||||
|
||||
@ -51,8 +51,6 @@ static BOOL AFSecKeyIsEqualToKey(SecKeyRef key1, SecKeyRef key2) {
|
||||
static id AFPublicKeyForCertificate(NSData *certificate) {
|
||||
id allowedPublicKey = nil;
|
||||
SecCertificateRef allowedCertificate;
|
||||
SecCertificateRef allowedCertificates[1];
|
||||
CFArrayRef tempCertificates = nil;
|
||||
SecPolicyRef policy = nil;
|
||||
SecTrustRef allowedTrust = nil;
|
||||
SecTrustResultType result;
|
||||
@ -60,12 +58,12 @@ static id AFPublicKeyForCertificate(NSData *certificate) {
|
||||
allowedCertificate = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificate);
|
||||
__Require_Quiet(allowedCertificate != NULL, _out);
|
||||
|
||||
allowedCertificates[0] = allowedCertificate;
|
||||
tempCertificates = CFArrayCreate(NULL, (const void **)allowedCertificates, 1, NULL);
|
||||
|
||||
policy = SecPolicyCreateBasicX509();
|
||||
__Require_noErr_Quiet(SecTrustCreateWithCertificates(tempCertificates, policy, &allowedTrust), _out);
|
||||
__Require_noErr_Quiet(SecTrustCreateWithCertificates(allowedCertificate, policy, &allowedTrust), _out);
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
__Require_noErr_Quiet(SecTrustEvaluate(allowedTrust, &result), _out);
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
allowedPublicKey = (__bridge_transfer id)SecTrustCopyPublicKey(allowedTrust);
|
||||
|
||||
@ -78,10 +76,6 @@ _out:
|
||||
CFRelease(policy);
|
||||
}
|
||||
|
||||
if (tempCertificates) {
|
||||
CFRelease(tempCertificates);
|
||||
}
|
||||
|
||||
if (allowedCertificate) {
|
||||
CFRelease(allowedCertificate);
|
||||
}
|
||||
@ -92,7 +86,10 @@ _out:
|
||||
static BOOL AFServerTrustIsValid(SecTrustRef serverTrust) {
|
||||
BOOL isValid = NO;
|
||||
SecTrustResultType result;
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
__Require_noErr_Quiet(SecTrustEvaluate(serverTrust, &result), _out);
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
isValid = (result == kSecTrustResultUnspecified || result == kSecTrustResultProceed);
|
||||
|
||||
@ -124,10 +121,11 @@ static NSArray * AFPublicKeyTrustChainForServerTrust(SecTrustRef serverTrust) {
|
||||
|
||||
SecTrustRef trust;
|
||||
__Require_noErr_Quiet(SecTrustCreateWithCertificates(certificates, policy, &trust), _out);
|
||||
|
||||
SecTrustResultType result;
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
__Require_noErr_Quiet(SecTrustEvaluate(trust, &result), _out);
|
||||
|
||||
#pragma clang diagnostic pop
|
||||
[trustChain addObject:(__bridge_transfer id)SecTrustCopyPublicKey(trust)];
|
||||
|
||||
_out:
|
||||
@ -167,17 +165,6 @@ static NSArray * AFPublicKeyTrustChainForServerTrust(SecTrustRef serverTrust) {
|
||||
return [NSSet setWithSet:certificates];
|
||||
}
|
||||
|
||||
+ (NSSet *)defaultPinnedCertificates {
|
||||
static NSSet *_defaultPinnedCertificates = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
NSBundle *bundle = [NSBundle bundleForClass:[self class]];
|
||||
_defaultPinnedCertificates = [self certificatesInBundle:bundle];
|
||||
});
|
||||
|
||||
return _defaultPinnedCertificates;
|
||||
}
|
||||
|
||||
+ (instancetype)defaultPolicy {
|
||||
AFSecurityPolicy *securityPolicy = [[self alloc] init];
|
||||
securityPolicy.SSLPinningMode = AFSSLPinningModeNone;
|
||||
@ -186,7 +173,8 @@ static NSArray * AFPublicKeyTrustChainForServerTrust(SecTrustRef serverTrust) {
|
||||
}
|
||||
|
||||
+ (instancetype)policyWithPinningMode:(AFSSLPinningMode)pinningMode {
|
||||
return [self policyWithPinningMode:pinningMode withPinnedCertificates:[self defaultPinnedCertificates]];
|
||||
NSSet <NSData *> *defaultPinnedCertificates = [self certificatesInBundle:[NSBundle mainBundle]];
|
||||
return [self policyWithPinningMode:pinningMode withPinnedCertificates:defaultPinnedCertificates];
|
||||
}
|
||||
|
||||
+ (instancetype)policyWithPinningMode:(AFSSLPinningMode)pinningMode withPinnedCertificates:(NSSet *)pinnedCertificates {
|
||||
@ -256,14 +244,11 @@ static NSArray * AFPublicKeyTrustChainForServerTrust(SecTrustRef serverTrust) {
|
||||
|
||||
if (self.SSLPinningMode == AFSSLPinningModeNone) {
|
||||
return self.allowInvalidCertificates || AFServerTrustIsValid(serverTrust);
|
||||
} else if (!AFServerTrustIsValid(serverTrust) && !self.allowInvalidCertificates) {
|
||||
} else if (!self.allowInvalidCertificates && !AFServerTrustIsValid(serverTrust)) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
switch (self.SSLPinningMode) {
|
||||
case AFSSLPinningModeNone:
|
||||
default:
|
||||
return NO;
|
||||
case AFSSLPinningModeCertificate: {
|
||||
NSMutableArray *pinnedCertificates = [NSMutableArray array];
|
||||
for (NSData *certificateData in self.pinnedCertificates) {
|
||||
@ -299,6 +284,9 @@ static NSArray * AFPublicKeyTrustChainForServerTrust(SecTrustRef serverTrust) {
|
||||
}
|
||||
return trustedPublicKeyCount > 0;
|
||||
}
|
||||
|
||||
default:
|
||||
return NO;
|
||||
}
|
||||
|
||||
return NO;
|
||||
@ -326,7 +314,7 @@ static NSArray * AFPublicKeyTrustChainForServerTrust(SecTrustRef serverTrust) {
|
||||
self.SSLPinningMode = [[decoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(SSLPinningMode))] unsignedIntegerValue];
|
||||
self.allowInvalidCertificates = [decoder decodeBoolForKey:NSStringFromSelector(@selector(allowInvalidCertificates))];
|
||||
self.validatesDomainName = [decoder decodeBoolForKey:NSStringFromSelector(@selector(validatesDomainName))];
|
||||
self.pinnedCertificates = [decoder decodeObjectOfClass:[NSArray class] forKey:NSStringFromSelector(@selector(pinnedCertificates))];
|
||||
self.pinnedCertificates = [decoder decodeObjectOfClass:[NSSet class] forKey:NSStringFromSelector(@selector(pinnedCertificates))];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@ -216,7 +216,7 @@ forHTTPHeaderField:(NSString *)field;
|
||||
|
||||
@param block A block that defines a process of encoding parameters into a query string. This block returns the query string and takes three arguments: the request, the parameters to encode, and the error that occurred when attempting to encode parameters for the given request.
|
||||
*/
|
||||
- (void)setQueryStringSerializationWithBlock:(nullable NSString * (^)(NSURLRequest *request, id parameters, NSError * __autoreleasing *error))block;
|
||||
- (void)setQueryStringSerializationWithBlock:(nullable NSString * _Nullable (^)(NSURLRequest *request, id parameters, NSError * __autoreleasing *error))block;
|
||||
|
||||
///-------------------------------
|
||||
/// @name Creating Request Objects
|
||||
@ -234,10 +234,10 @@ forHTTPHeaderField:(NSString *)field;
|
||||
|
||||
@return An `NSMutableURLRequest` object.
|
||||
*/
|
||||
- (NSMutableURLRequest *)requestWithMethod:(NSString *)method
|
||||
URLString:(NSString *)URLString
|
||||
parameters:(nullable id)parameters
|
||||
error:(NSError * _Nullable __autoreleasing *)error;
|
||||
- (nullable NSMutableURLRequest *)requestWithMethod:(NSString *)method
|
||||
URLString:(NSString *)URLString
|
||||
parameters:(nullable id)parameters
|
||||
error:(NSError * _Nullable __autoreleasing *)error;
|
||||
|
||||
/**
|
||||
Creates an `NSMutableURLRequest` object with the specified HTTP method and URLString, and constructs a `multipart/form-data` HTTP body, using the specified parameters and multipart form data block. See http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4.2
|
||||
|
||||
@ -60,10 +60,7 @@ NSString * AFPercentEscapedStringFromString(NSString *string) {
|
||||
NSMutableString *escaped = @"".mutableCopy;
|
||||
|
||||
while (index < string.length) {
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wgnu"
|
||||
NSUInteger length = MIN(string.length - index, batchSize);
|
||||
#pragma GCC diagnostic pop
|
||||
NSRange range = NSMakeRange(index, length);
|
||||
|
||||
// To avoid breaking up character sequences such as 👴🏻👮🏽
|
||||
@ -189,6 +186,7 @@ static void *AFHTTPRequestSerializerObserverContext = &AFHTTPRequestSerializerOb
|
||||
@interface AFHTTPRequestSerializer ()
|
||||
@property (readwrite, nonatomic, strong) NSMutableSet *mutableObservedChangedKeyPaths;
|
||||
@property (readwrite, nonatomic, strong) NSMutableDictionary *mutableHTTPRequestHeaders;
|
||||
@property (readwrite, nonatomic, strong) dispatch_queue_t requestHeaderModificationQueue;
|
||||
@property (readwrite, nonatomic, assign) AFHTTPRequestQueryStringSerializationStyle queryStringSerializationStyle;
|
||||
@property (readwrite, nonatomic, copy) AFQueryStringSerializationBlock queryStringSerialization;
|
||||
@end
|
||||
@ -208,6 +206,7 @@ static void *AFHTTPRequestSerializerObserverContext = &AFHTTPRequestSerializerOb
|
||||
self.stringEncoding = NSUTF8StringEncoding;
|
||||
|
||||
self.mutableHTTPRequestHeaders = [NSMutableDictionary dictionary];
|
||||
self.requestHeaderModificationQueue = dispatch_queue_create("requestHeaderModificationQueue", DISPATCH_QUEUE_CONCURRENT);
|
||||
|
||||
// Accept-Language HTTP Header; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4
|
||||
NSMutableArray *acceptLanguagesComponents = [NSMutableArray array];
|
||||
@ -218,19 +217,17 @@ static void *AFHTTPRequestSerializerObserverContext = &AFHTTPRequestSerializerOb
|
||||
}];
|
||||
[self setValue:[acceptLanguagesComponents componentsJoinedByString:@", "] forHTTPHeaderField:@"Accept-Language"];
|
||||
|
||||
// User-Agent Header; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.43
|
||||
NSString *userAgent = nil;
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wgnu"
|
||||
#if TARGET_OS_IOS
|
||||
// User-Agent Header; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.43
|
||||
userAgent = [NSString stringWithFormat:@"%@/%@ (%@; iOS %@; Scale/%0.2f)", [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleExecutableKey] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleIdentifierKey], [[NSBundle mainBundle] infoDictionary][@"CFBundleShortVersionString"] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleVersionKey], [[UIDevice currentDevice] model], [[UIDevice currentDevice] systemVersion], [[UIScreen mainScreen] scale]];
|
||||
#elif TARGET_OS_TV
|
||||
userAgent = [NSString stringWithFormat:@"%@/%@ (%@; tvOS %@; Scale/%0.2f)", [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleExecutableKey] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleIdentifierKey], [[NSBundle mainBundle] infoDictionary][@"CFBundleShortVersionString"] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleVersionKey], [[UIDevice currentDevice] model], [[UIDevice currentDevice] systemVersion], [[UIScreen mainScreen] scale]];
|
||||
#elif TARGET_OS_WATCH
|
||||
// User-Agent Header; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.43
|
||||
userAgent = [NSString stringWithFormat:@"%@/%@ (%@; watchOS %@; Scale/%0.2f)", [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleExecutableKey] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleIdentifierKey], [[NSBundle mainBundle] infoDictionary][@"CFBundleShortVersionString"] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleVersionKey], [[WKInterfaceDevice currentDevice] model], [[WKInterfaceDevice currentDevice] systemVersion], [[WKInterfaceDevice currentDevice] screenScale]];
|
||||
#elif defined(__MAC_OS_X_VERSION_MIN_REQUIRED)
|
||||
userAgent = [NSString stringWithFormat:@"%@/%@ (Mac OS X %@)", [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleExecutableKey] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleIdentifierKey], [[NSBundle mainBundle] infoDictionary][@"CFBundleShortVersionString"] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleVersionKey], [[NSProcessInfo processInfo] operatingSystemVersionString]];
|
||||
#endif
|
||||
#pragma clang diagnostic pop
|
||||
if (userAgent) {
|
||||
if (![userAgent canBeConvertedToEncoding:NSASCIIStringEncoding]) {
|
||||
NSMutableString *mutableUserAgent = [userAgent mutableCopy];
|
||||
@ -306,17 +303,27 @@ static void *AFHTTPRequestSerializerObserverContext = &AFHTTPRequestSerializerOb
|
||||
#pragma mark -
|
||||
|
||||
- (NSDictionary *)HTTPRequestHeaders {
|
||||
return [NSDictionary dictionaryWithDictionary:self.mutableHTTPRequestHeaders];
|
||||
NSDictionary __block *value;
|
||||
dispatch_sync(self.requestHeaderModificationQueue, ^{
|
||||
value = [NSDictionary dictionaryWithDictionary:self.mutableHTTPRequestHeaders];
|
||||
});
|
||||
return value;
|
||||
}
|
||||
|
||||
- (void)setValue:(NSString *)value
|
||||
forHTTPHeaderField:(NSString *)field
|
||||
{
|
||||
[self.mutableHTTPRequestHeaders setValue:value forKey:field];
|
||||
dispatch_barrier_sync(self.requestHeaderModificationQueue, ^{
|
||||
[self.mutableHTTPRequestHeaders setValue:value forKey:field];
|
||||
});
|
||||
}
|
||||
|
||||
- (NSString *)valueForHTTPHeaderField:(NSString *)field {
|
||||
return [self.mutableHTTPRequestHeaders valueForKey:field];
|
||||
NSString __block *value;
|
||||
dispatch_sync(self.requestHeaderModificationQueue, ^{
|
||||
value = [self.mutableHTTPRequestHeaders valueForKey:field];
|
||||
});
|
||||
return value;
|
||||
}
|
||||
|
||||
- (void)setAuthorizationHeaderFieldWithUsername:(NSString *)username
|
||||
@ -328,7 +335,9 @@ forHTTPHeaderField:(NSString *)field
|
||||
}
|
||||
|
||||
- (void)clearAuthorizationHeader {
|
||||
[self.mutableHTTPRequestHeaders removeObjectForKey:@"Authorization"];
|
||||
dispatch_barrier_sync(self.requestHeaderModificationQueue, ^{
|
||||
[self.mutableHTTPRequestHeaders removeObjectForKey:@"Authorization"];
|
||||
});
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
@ -560,15 +569,19 @@ forHTTPHeaderField:(NSString *)field
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(NSCoder *)coder {
|
||||
[coder encodeObject:self.mutableHTTPRequestHeaders forKey:NSStringFromSelector(@selector(mutableHTTPRequestHeaders))];
|
||||
[coder encodeInteger:self.queryStringSerializationStyle forKey:NSStringFromSelector(@selector(queryStringSerializationStyle))];
|
||||
dispatch_sync(self.requestHeaderModificationQueue, ^{
|
||||
[coder encodeObject:self.mutableHTTPRequestHeaders forKey:NSStringFromSelector(@selector(mutableHTTPRequestHeaders))];
|
||||
});
|
||||
[coder encodeObject:@(self.queryStringSerializationStyle) forKey:NSStringFromSelector(@selector(queryStringSerializationStyle))];
|
||||
}
|
||||
|
||||
#pragma mark - NSCopying
|
||||
|
||||
- (instancetype)copyWithZone:(NSZone *)zone {
|
||||
AFHTTPRequestSerializer *serializer = [[[self class] allocWithZone:zone] init];
|
||||
serializer.mutableHTTPRequestHeaders = [self.mutableHTTPRequestHeaders mutableCopyWithZone:zone];
|
||||
dispatch_sync(self.requestHeaderModificationQueue, ^{
|
||||
serializer.mutableHTTPRequestHeaders = [self.mutableHTTPRequestHeaders mutableCopyWithZone:zone];
|
||||
});
|
||||
serializer.queryStringSerializationStyle = self.queryStringSerializationStyle;
|
||||
serializer.queryStringSerialization = self.queryStringSerialization;
|
||||
|
||||
@ -667,6 +680,11 @@ NSTimeInterval const kAFUploadStream3GSuggestedDelay = 0.2;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setRequest:(NSMutableURLRequest *)request
|
||||
{
|
||||
_request = [request mutableCopy];
|
||||
}
|
||||
|
||||
- (BOOL)appendPartWithFileURL:(NSURL *)fileURL
|
||||
name:(NSString *)name
|
||||
error:(NSError * __autoreleasing *)error
|
||||
@ -835,14 +853,11 @@ NSTimeInterval const kAFUploadStream3GSuggestedDelay = 0.2;
|
||||
@end
|
||||
|
||||
@implementation AFMultipartBodyStream
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wimplicit-atomic-properties"
|
||||
#if (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000) || (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 1100)
|
||||
@synthesize delegate;
|
||||
#endif
|
||||
@synthesize streamStatus;
|
||||
@synthesize streamError;
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
- (instancetype)initWithStringEncoding:(NSStringEncoding)encoding {
|
||||
self = [super init];
|
||||
@ -888,8 +903,6 @@ NSTimeInterval const kAFUploadStream3GSuggestedDelay = 0.2;
|
||||
|
||||
NSInteger totalNumberOfBytesRead = 0;
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wgnu"
|
||||
while ((NSUInteger)totalNumberOfBytesRead < MIN(length, self.numberOfBytesInPacket)) {
|
||||
if (!self.currentHTTPBodyPart || ![self.currentHTTPBodyPart hasBytesAvailable]) {
|
||||
if (!(self.currentHTTPBodyPart = [self.HTTPBodyPartEnumerator nextObject])) {
|
||||
@ -910,7 +923,6 @@ NSTimeInterval const kAFUploadStream3GSuggestedDelay = 0.2;
|
||||
}
|
||||
}
|
||||
}
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
return totalNumberOfBytesRead;
|
||||
}
|
||||
@ -1091,8 +1103,6 @@ typedef enum {
|
||||
return YES;
|
||||
}
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wcovered-switch-default"
|
||||
switch (self.inputStream.streamStatus) {
|
||||
case NSStreamStatusNotOpen:
|
||||
case NSStreamStatusOpening:
|
||||
@ -1106,7 +1116,6 @@ typedef enum {
|
||||
default:
|
||||
return NO;
|
||||
}
|
||||
#pragma clang diagnostic pop
|
||||
}
|
||||
|
||||
- (NSInteger)read:(uint8_t *)buffer
|
||||
@ -1151,11 +1160,8 @@ typedef enum {
|
||||
intoBuffer:(uint8_t *)buffer
|
||||
maxLength:(NSUInteger)length
|
||||
{
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wgnu"
|
||||
NSRange range = NSMakeRange((NSUInteger)_phaseReadOffset, MIN([data length] - ((NSUInteger)_phaseReadOffset), length));
|
||||
[data getBytes:buffer range:range];
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
_phaseReadOffset += range.length;
|
||||
|
||||
@ -1174,8 +1180,6 @@ typedef enum {
|
||||
return YES;
|
||||
}
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wcovered-switch-default"
|
||||
switch (_phase) {
|
||||
case AFEncapsulationBoundaryPhase:
|
||||
_phase = AFHeaderPhase;
|
||||
@ -1195,7 +1199,6 @@ typedef enum {
|
||||
break;
|
||||
}
|
||||
_phaseReadOffset = 0;
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
return YES;
|
||||
}
|
||||
@ -1257,7 +1260,21 @@ typedef enum {
|
||||
[mutableRequest setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
|
||||
}
|
||||
|
||||
[mutableRequest setHTTPBody:[NSJSONSerialization dataWithJSONObject:parameters options:self.writingOptions error:error]];
|
||||
if (![NSJSONSerialization isValidJSONObject:parameters]) {
|
||||
if (error) {
|
||||
NSDictionary *userInfo = @{NSLocalizedFailureReasonErrorKey: NSLocalizedStringFromTable(@"The `parameters` argument is not valid JSON.", @"AFNetworking", nil)};
|
||||
*error = [[NSError alloc] initWithDomain:AFURLRequestSerializationErrorDomain code:NSURLErrorCannotDecodeContentData userInfo:userInfo];
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:parameters options:self.writingOptions error:error];
|
||||
|
||||
if (!jsonData) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
[mutableRequest setHTTPBody:jsonData];
|
||||
}
|
||||
|
||||
return mutableRequest;
|
||||
@ -1279,7 +1296,7 @@ typedef enum {
|
||||
- (void)encodeWithCoder:(NSCoder *)coder {
|
||||
[super encodeWithCoder:coder];
|
||||
|
||||
[coder encodeInteger:self.writingOptions forKey:NSStringFromSelector(@selector(writingOptions))];
|
||||
[coder encodeObject:@(self.writingOptions) forKey:NSStringFromSelector(@selector(writingOptions))];
|
||||
}
|
||||
|
||||
#pragma mark - NSCopying
|
||||
@ -1336,7 +1353,13 @@ typedef enum {
|
||||
[mutableRequest setValue:@"application/x-plist" forHTTPHeaderField:@"Content-Type"];
|
||||
}
|
||||
|
||||
[mutableRequest setHTTPBody:[NSPropertyListSerialization dataWithPropertyList:parameters format:self.format options:self.writeOptions error:error]];
|
||||
NSData *plistData = [NSPropertyListSerialization dataWithPropertyList:parameters format:self.format options:self.writeOptions error:error];
|
||||
|
||||
if (!plistData) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
[mutableRequest setHTTPBody:plistData];
|
||||
}
|
||||
|
||||
return mutableRequest;
|
||||
@ -1359,7 +1382,7 @@ typedef enum {
|
||||
- (void)encodeWithCoder:(NSCoder *)coder {
|
||||
[super encodeWithCoder:coder];
|
||||
|
||||
[coder encodeInteger:self.format forKey:NSStringFromSelector(@selector(format))];
|
||||
[coder encodeObject:@(self.format) forKey:NSStringFromSelector(@selector(format))];
|
||||
[coder encodeObject:@(self.writeOptions) forKey:NSStringFromSelector(@selector(writeOptions))];
|
||||
}
|
||||
|
||||
|
||||
@ -24,6 +24,11 @@
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
Recursively removes `NSNull` values from a JSON object.
|
||||
*/
|
||||
FOUNDATION_EXPORT id AFJSONObjectByRemovingKeysWithNullValues(id JSONObject, NSJSONReadingOptions readingOptions);
|
||||
|
||||
/**
|
||||
The `AFURLResponseSerialization` protocol is adopted by an object that decodes data into a more useful object representation, according to details in the server response. Response serializers may additionally perform validation on the incoming response and data.
|
||||
|
||||
@ -57,11 +62,6 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
- (instancetype)init;
|
||||
|
||||
/**
|
||||
The string encoding used to serialize data received from the server, when no string encoding is specified by the response. `NSUTF8StringEncoding` by default.
|
||||
*/
|
||||
@property (nonatomic, assign) NSStringEncoding stringEncoding;
|
||||
|
||||
/**
|
||||
Creates and returns a serializer with default configuration.
|
||||
*/
|
||||
@ -111,6 +111,8 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
- `application/json`
|
||||
- `text/json`
|
||||
- `text/javascript`
|
||||
|
||||
In RFC 7159 - Section 8.1, it states that JSON text is required to be encoded in UTF-8, UTF-16, or UTF-32, and the default encoding is UTF-8. NSJSONSerialization provides support for all the encodings listed in the specification, and recommends UTF-8 for efficiency. Using an unsupported encoding will result in serialization error. See the `NSJSONSerialization` documentation for more details.
|
||||
*/
|
||||
@interface AFJSONResponseSerializer : AFHTTPResponseSerializer
|
||||
|
||||
@ -166,7 +168,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
- (instancetype)init;
|
||||
|
||||
/**
|
||||
Input and output options specifically intended for `NSXMLDocument` objects. For possible values, see the `NSJSONSerialization` documentation section "NSJSONReadingOptions". `0` by default.
|
||||
Input and output options specifically intended for `NSXMLDocument` objects. For possible values, see the `NSXMLDocument` documentation section "Input and Output Options". `0` by default.
|
||||
*/
|
||||
@property (nonatomic, assign) NSUInteger options;
|
||||
|
||||
|
||||
@ -60,11 +60,13 @@ static BOOL AFErrorOrUnderlyingErrorHasCodeInDomain(NSError *error, NSInteger co
|
||||
return NO;
|
||||
}
|
||||
|
||||
static id AFJSONObjectByRemovingKeysWithNullValues(id JSONObject, NSJSONReadingOptions readingOptions) {
|
||||
id AFJSONObjectByRemovingKeysWithNullValues(id JSONObject, NSJSONReadingOptions readingOptions) {
|
||||
if ([JSONObject isKindOfClass:[NSArray class]]) {
|
||||
NSMutableArray *mutableArray = [NSMutableArray arrayWithCapacity:[(NSArray *)JSONObject count]];
|
||||
for (id value in (NSArray *)JSONObject) {
|
||||
[mutableArray addObject:AFJSONObjectByRemovingKeysWithNullValues(value, readingOptions)];
|
||||
if (![value isEqual:[NSNull null]]) {
|
||||
[mutableArray addObject:AFJSONObjectByRemovingKeysWithNullValues(value, readingOptions)];
|
||||
}
|
||||
}
|
||||
|
||||
return (readingOptions & NSJSONReadingMutableContainers) ? mutableArray : [NSArray arrayWithArray:mutableArray];
|
||||
@ -97,8 +99,6 @@ static id AFJSONObjectByRemovingKeysWithNullValues(id JSONObject, NSJSONReadingO
|
||||
return nil;
|
||||
}
|
||||
|
||||
self.stringEncoding = NSUTF8StringEncoding;
|
||||
|
||||
self.acceptableStatusCodes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(200, 100)];
|
||||
self.acceptableContentTypes = nil;
|
||||
|
||||
@ -114,7 +114,7 @@ static id AFJSONObjectByRemovingKeysWithNullValues(id JSONObject, NSJSONReadingO
|
||||
BOOL responseIsValid = YES;
|
||||
NSError *validationError = nil;
|
||||
|
||||
if (response && [response isKindOfClass:[NSHTTPURLResponse class]]) {
|
||||
if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
|
||||
if (self.acceptableContentTypes && ![self.acceptableContentTypes containsObject:[response MIMEType]] &&
|
||||
!([response MIMEType] == nil && [data length] == 0)) {
|
||||
|
||||
@ -182,7 +182,7 @@ static id AFJSONObjectByRemovingKeysWithNullValues(id JSONObject, NSJSONReadingO
|
||||
}
|
||||
|
||||
self.acceptableStatusCodes = [decoder decodeObjectOfClass:[NSIndexSet class] forKey:NSStringFromSelector(@selector(acceptableStatusCodes))];
|
||||
self.acceptableContentTypes = [decoder decodeObjectOfClass:[NSIndexSet class] forKey:NSStringFromSelector(@selector(acceptableContentTypes))];
|
||||
self.acceptableContentTypes = [decoder decodeObjectOfClass:[NSSet class] forKey:NSStringFromSelector(@selector(acceptableContentTypes))];
|
||||
|
||||
return self;
|
||||
}
|
||||
@ -242,23 +242,28 @@ static id AFJSONObjectByRemovingKeysWithNullValues(id JSONObject, NSJSONReadingO
|
||||
}
|
||||
}
|
||||
|
||||
id responseObject = nil;
|
||||
NSError *serializationError = nil;
|
||||
// Workaround for behavior of Rails to return a single space for `head :ok` (a workaround for a bug in Safari), which is not interpreted as valid input by NSJSONSerialization.
|
||||
// See https://github.com/rails/rails/issues/1742
|
||||
BOOL isSpace = [data isEqualToData:[NSData dataWithBytes:" " length:1]];
|
||||
if (data.length > 0 && !isSpace) {
|
||||
responseObject = [NSJSONSerialization JSONObjectWithData:data options:self.readingOptions error:&serializationError];
|
||||
} else {
|
||||
|
||||
if (data.length == 0 || isSpace) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSError *serializationError = nil;
|
||||
|
||||
id responseObject = [NSJSONSerialization JSONObjectWithData:data options:self.readingOptions error:&serializationError];
|
||||
|
||||
if (self.removesKeysWithNullValues && responseObject) {
|
||||
responseObject = AFJSONObjectByRemovingKeysWithNullValues(responseObject, self.readingOptions);
|
||||
if (!responseObject)
|
||||
{
|
||||
if (error) {
|
||||
*error = AFErrorWithUnderlyingError(serializationError, *error);
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
if (error) {
|
||||
*error = AFErrorWithUnderlyingError(serializationError, *error);
|
||||
|
||||
if (self.removesKeysWithNullValues) {
|
||||
return AFJSONObjectByRemovingKeysWithNullValues(responseObject, self.readingOptions);
|
||||
}
|
||||
|
||||
return responseObject;
|
||||
@ -266,6 +271,10 @@ static id AFJSONObjectByRemovingKeysWithNullValues(id JSONObject, NSJSONReadingO
|
||||
|
||||
#pragma mark - NSSecureCoding
|
||||
|
||||
+ (BOOL)supportsSecureCoding {
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (instancetype)initWithCoder:(NSCoder *)decoder {
|
||||
self = [super initWithCoder:decoder];
|
||||
if (!self) {
|
||||
@ -288,7 +297,7 @@ static id AFJSONObjectByRemovingKeysWithNullValues(id JSONObject, NSJSONReadingO
|
||||
#pragma mark - NSCopying
|
||||
|
||||
- (instancetype)copyWithZone:(NSZone *)zone {
|
||||
AFJSONResponseSerializer *serializer = [[[self class] allocWithZone:zone] init];
|
||||
AFJSONResponseSerializer *serializer = [super copyWithZone:zone];
|
||||
serializer.readingOptions = self.readingOptions;
|
||||
serializer.removesKeysWithNullValues = self.removesKeysWithNullValues;
|
||||
|
||||
@ -378,10 +387,14 @@ static id AFJSONObjectByRemovingKeysWithNullValues(id JSONObject, NSJSONReadingO
|
||||
NSError *serializationError = nil;
|
||||
NSXMLDocument *document = [[NSXMLDocument alloc] initWithData:data options:self.options error:&serializationError];
|
||||
|
||||
if (error) {
|
||||
*error = AFErrorWithUnderlyingError(serializationError, *error);
|
||||
if (!document)
|
||||
{
|
||||
if (error) {
|
||||
*error = AFErrorWithUnderlyingError(serializationError, *error);
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
|
||||
return document;
|
||||
}
|
||||
|
||||
@ -407,7 +420,7 @@ static id AFJSONObjectByRemovingKeysWithNullValues(id JSONObject, NSJSONReadingO
|
||||
#pragma mark - NSCopying
|
||||
|
||||
- (instancetype)copyWithZone:(NSZone *)zone {
|
||||
AFXMLDocumentResponseSerializer *serializer = [[[self class] allocWithZone:zone] init];
|
||||
AFXMLDocumentResponseSerializer *serializer = [super copyWithZone:zone];
|
||||
serializer.options = self.options;
|
||||
|
||||
return serializer;
|
||||
@ -458,15 +471,20 @@ static id AFJSONObjectByRemovingKeysWithNullValues(id JSONObject, NSJSONReadingO
|
||||
}
|
||||
}
|
||||
|
||||
id responseObject;
|
||||
NSError *serializationError = nil;
|
||||
|
||||
if (data) {
|
||||
responseObject = [NSPropertyListSerialization propertyListWithData:data options:self.readOptions format:NULL error:&serializationError];
|
||||
if (!data) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
if (error) {
|
||||
*error = AFErrorWithUnderlyingError(serializationError, *error);
|
||||
|
||||
NSError *serializationError = nil;
|
||||
|
||||
id responseObject = [NSPropertyListSerialization propertyListWithData:data options:self.readOptions format:NULL error:&serializationError];
|
||||
|
||||
if (!responseObject)
|
||||
{
|
||||
if (error) {
|
||||
*error = AFErrorWithUnderlyingError(serializationError, *error);
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
return responseObject;
|
||||
@ -474,6 +492,10 @@ static id AFJSONObjectByRemovingKeysWithNullValues(id JSONObject, NSJSONReadingO
|
||||
|
||||
#pragma mark - NSSecureCoding
|
||||
|
||||
+ (BOOL)supportsSecureCoding {
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (instancetype)initWithCoder:(NSCoder *)decoder {
|
||||
self = [super initWithCoder:decoder];
|
||||
if (!self) {
|
||||
@ -496,7 +518,7 @@ static id AFJSONObjectByRemovingKeysWithNullValues(id JSONObject, NSJSONReadingO
|
||||
#pragma mark - NSCopying
|
||||
|
||||
- (instancetype)copyWithZone:(NSZone *)zone {
|
||||
AFPropertyListResponseSerializer *serializer = [[[self class] allocWithZone:zone] init];
|
||||
AFPropertyListResponseSerializer *serializer = [super copyWithZone:zone];
|
||||
serializer.format = self.format;
|
||||
serializer.readOptions = self.readOptions;
|
||||
|
||||
@ -690,6 +712,10 @@ static UIImage * AFInflatedImageFromResponseWithDataAtScale(NSHTTPURLResponse *r
|
||||
|
||||
#pragma mark - NSSecureCoding
|
||||
|
||||
+ (BOOL)supportsSecureCoding {
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (instancetype)initWithCoder:(NSCoder *)decoder {
|
||||
self = [super initWithCoder:decoder];
|
||||
if (!self) {
|
||||
@ -722,7 +748,7 @@ static UIImage * AFInflatedImageFromResponseWithDataAtScale(NSHTTPURLResponse *r
|
||||
#pragma mark - NSCopying
|
||||
|
||||
- (instancetype)copyWithZone:(NSZone *)zone {
|
||||
AFImageResponseSerializer *serializer = [[[self class] allocWithZone:zone] init];
|
||||
AFImageResponseSerializer *serializer = [super copyWithZone:zone];
|
||||
|
||||
#if TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_WATCH
|
||||
serializer.imageScale = self.imageScale;
|
||||
@ -776,13 +802,18 @@ static UIImage * AFInflatedImageFromResponseWithDataAtScale(NSHTTPURLResponse *r
|
||||
|
||||
#pragma mark - NSSecureCoding
|
||||
|
||||
+ (BOOL)supportsSecureCoding {
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (instancetype)initWithCoder:(NSCoder *)decoder {
|
||||
self = [super initWithCoder:decoder];
|
||||
if (!self) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
self.responseSerializers = [decoder decodeObjectOfClass:[NSArray class] forKey:NSStringFromSelector(@selector(responseSerializers))];
|
||||
NSSet *classes = [NSSet setWithArray:@[[NSArray class], [AFHTTPResponseSerializer <AFURLResponseSerialization> class]]];
|
||||
self.responseSerializers = [decoder decodeObjectOfClasses:classes forKey:NSStringFromSelector(@selector(responseSerializers))];
|
||||
|
||||
return self;
|
||||
}
|
||||
@ -796,7 +827,7 @@ static UIImage * AFInflatedImageFromResponseWithDataAtScale(NSHTTPURLResponse *r
|
||||
#pragma mark - NSCopying
|
||||
|
||||
- (instancetype)copyWithZone:(NSZone *)zone {
|
||||
AFCompoundResponseSerializer *serializer = [[[self class] allocWithZone:zone] init];
|
||||
AFCompoundResponseSerializer *serializer = [super copyWithZone:zone];
|
||||
serializer.responseSerializers = self.responseSerializers;
|
||||
|
||||
return serializer;
|
||||
|
||||
@ -25,6 +25,7 @@
|
||||
#import "AFURLResponseSerialization.h"
|
||||
#import "AFURLRequestSerialization.h"
|
||||
#import "AFSecurityPolicy.h"
|
||||
#import "AFCompatibilityMacros.h"
|
||||
#if !TARGET_OS_WATCH
|
||||
#import "AFNetworkReachabilityManager.h"
|
||||
#endif
|
||||
@ -64,7 +65,7 @@
|
||||
### `NSURLSessionDownloadDelegate`
|
||||
|
||||
- `URLSession:downloadTask:didFinishDownloadingToURL:`
|
||||
- `URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesWritten:totalBytesExpectedToWrite:`
|
||||
- `URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:`
|
||||
- `URLSession:downloadTask:didResumeAtOffset:expectedTotalBytes:`
|
||||
|
||||
If any of these methods are overridden in a subclass, they _must_ call the `super` implementation first.
|
||||
@ -164,19 +165,6 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
*/
|
||||
@property (nonatomic, strong, nullable) dispatch_group_t completionGroup;
|
||||
|
||||
///---------------------------------
|
||||
/// @name Working Around System Bugs
|
||||
///---------------------------------
|
||||
|
||||
/**
|
||||
Whether to attempt to retry creation of upload tasks for background sessions when initial call returns `nil`. `NO` by default.
|
||||
|
||||
@bug As of iOS 7.0, there is a bug where upload tasks created for background tasks are sometimes `nil`. As a workaround, if this property is `YES`, AFNetworking will follow Apple's recommendation to try creating the task again.
|
||||
|
||||
@see https://github.com/AFNetworking/AFNetworking/issues/1675
|
||||
*/
|
||||
@property (nonatomic, assign) BOOL attemptsToRecreateUploadTasksForBackgroundSessions;
|
||||
|
||||
///---------------------
|
||||
/// @name Initialization
|
||||
///---------------------
|
||||
@ -191,25 +179,17 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
- (instancetype)initWithSessionConfiguration:(nullable NSURLSessionConfiguration *)configuration NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
/**
|
||||
Invalidates the managed session, optionally canceling pending tasks.
|
||||
|
||||
@param cancelPendingTasks Whether or not to cancel pending tasks.
|
||||
Invalidates the managed session, optionally canceling pending tasks and optionally resets given session.
|
||||
|
||||
@param cancelPendingTasks Whether or not to cancel pending tasks.
|
||||
@param resetSession Whether or not to reset the session of the manager.
|
||||
*/
|
||||
- (void)invalidateSessionCancelingTasks:(BOOL)cancelPendingTasks;
|
||||
- (void)invalidateSessionCancelingTasks:(BOOL)cancelPendingTasks resetSession:(BOOL)resetSession;
|
||||
|
||||
///-------------------------
|
||||
/// @name Running Data Tasks
|
||||
///-------------------------
|
||||
|
||||
/**
|
||||
Creates an `NSURLSessionDataTask` with the specified request.
|
||||
|
||||
@param request The HTTP request for the request.
|
||||
@param completionHandler A block object to be executed when the task finishes. This block has no return value and takes three arguments: the server response, the response object created by that serializer, and the error that occurred, if any.
|
||||
*/
|
||||
- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request
|
||||
completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError * _Nullable error))completionHandler;
|
||||
|
||||
/**
|
||||
Creates an `NSURLSessionDataTask` with the specified request.
|
||||
|
||||
@ -335,6 +315,11 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
Sets a block to be executed when a connection level authentication challenge has occurred, as handled by the `NSURLSessionDelegate` method `URLSession:didReceiveChallenge:completionHandler:`.
|
||||
|
||||
@param block A block object to be executed when a connection level authentication challenge has occurred. The block returns the disposition of the authentication challenge, and takes three arguments: the session, the authentication challenge, and a pointer to the credential that should be used to resolve the challenge.
|
||||
|
||||
@warning Implementing a session authentication challenge handler yourself totally bypasses AFNetworking's security policy defined in `AFSecurityPolicy`. Make sure you fully understand the implications before implementing a custom session authentication challenge handler. If you do not want to bypass AFNetworking's security policy, use `setTaskDidReceiveAuthenticationChallengeBlock:` instead.
|
||||
|
||||
@see -securityPolicy
|
||||
@see -setTaskDidReceiveAuthenticationChallengeBlock:
|
||||
*/
|
||||
- (void)setSessionDidReceiveAuthenticationChallengeBlock:(nullable NSURLSessionAuthChallengeDisposition (^)(NSURLSession *session, NSURLAuthenticationChallenge *challenge, NSURLCredential * _Nullable __autoreleasing * _Nullable credential))block;
|
||||
|
||||
@ -354,14 +339,27 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@param block A block object to be executed when an HTTP request is attempting to perform a redirection to a different URL. The block returns the request to be made for the redirection, and takes four arguments: the session, the task, the redirection response, and the request corresponding to the redirection response.
|
||||
*/
|
||||
- (void)setTaskWillPerformHTTPRedirectionBlock:(nullable NSURLRequest * (^)(NSURLSession *session, NSURLSessionTask *task, NSURLResponse *response, NSURLRequest *request))block;
|
||||
- (void)setTaskWillPerformHTTPRedirectionBlock:(nullable NSURLRequest * _Nullable (^)(NSURLSession *session, NSURLSessionTask *task, NSURLResponse *response, NSURLRequest *request))block;
|
||||
|
||||
/**
|
||||
Sets a block to be executed when a session task has received a request specific authentication challenge, as handled by the `NSURLSessionTaskDelegate` method `URLSession:task:didReceiveChallenge:completionHandler:`.
|
||||
|
||||
@param block A block object to be executed when a session task has received a request specific authentication challenge. The block returns the disposition of the authentication challenge, and takes four arguments: the session, the task, the authentication challenge, and a pointer to the credential that should be used to resolve the challenge.
|
||||
|
||||
@param authenticationChallengeHandler A block object to be executed when a session task has received a request specific authentication challenge.
|
||||
|
||||
When implementing an authentication challenge handler, you should check the authentication method first (`challenge.protectionSpace.authenticationMethod `) to decide if you want to handle the authentication challenge yourself or if you want AFNetworking to handle it. If you want AFNetworking to handle the authentication challenge, just return `@(NSURLSessionAuthChallengePerformDefaultHandling)`. For example, you certainly want AFNetworking to handle certificate validation (i.e. authentication method == `NSURLAuthenticationMethodServerTrust`) as defined by the security policy. If you want to handle the challenge yourself, you have four options:
|
||||
|
||||
1. Return `nil` from the authentication challenge handler. You **MUST** call the completion handler with a disposition and credentials yourself. Use this if you need to present a user interface to let the user enter their credentials.
|
||||
2. Return an `NSError` object from the authentication challenge handler. You **MUST NOT** call the completion handler when returning an `NSError `. The returned error will be reported in the completion handler of the task. Use this if you need to abort an authentication challenge with a specific error.
|
||||
3. Return an `NSURLCredential` object from the authentication challenge handler. You **MUST NOT** call the completion handler when returning an `NSURLCredential`. The returned credentials will be used to fulfil the challenge. Use this when you can get credentials without presenting a user interface.
|
||||
4. Return an `NSNumber` object wrapping an `NSURLSessionAuthChallengeDisposition`. Supported values are `@(NSURLSessionAuthChallengePerformDefaultHandling)`, `@(NSURLSessionAuthChallengeCancelAuthenticationChallenge)` and `@(NSURLSessionAuthChallengeRejectProtectionSpace)`. You **MUST NOT** call the completion handler when returning an `NSNumber`.
|
||||
|
||||
If you return anything else from the authentication challenge handler, an exception will be thrown.
|
||||
|
||||
For more information about how URL sessions handle the different types of authentication challenges, see [NSURLSession](https://developer.apple.com/reference/foundation/nsurlsession?language=objc) and [URL Session Programming Guide](https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/URLLoadingSystem/URLLoadingSystem.html).
|
||||
|
||||
@see -securityPolicy
|
||||
*/
|
||||
- (void)setTaskDidReceiveAuthenticationChallengeBlock:(nullable NSURLSessionAuthChallengeDisposition (^)(NSURLSession *session, NSURLSessionTask *task, NSURLAuthenticationChallenge *challenge, NSURLCredential * _Nullable __autoreleasing * _Nullable credential))block;
|
||||
- (void)setAuthenticationChallengeHandler:(id (^)(NSURLSession *session, NSURLSessionTask *task, NSURLAuthenticationChallenge *challenge, void (^completionHandler)(NSURLSessionAuthChallengeDisposition , NSURLCredential * _Nullable)))authenticationChallengeHandler;
|
||||
|
||||
/**
|
||||
Sets a block to be executed periodically to track upload progress, as handled by the `NSURLSessionTaskDelegate` method `URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:`.
|
||||
@ -377,6 +375,14 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
*/
|
||||
- (void)setTaskDidCompleteBlock:(nullable void (^)(NSURLSession *session, NSURLSessionTask *task, NSError * _Nullable error))block;
|
||||
|
||||
/**
|
||||
Sets a block to be executed when metrics are finalized related to a specific task, as handled by the `NSURLSessionTaskDelegate` method `URLSession:task:didFinishCollectingMetrics:`.
|
||||
|
||||
@param block A block object to be executed when a session task is completed. The block has no return value, and takes three arguments: the session, the task, and any metrics that were collected in the process of executing the task.
|
||||
*/
|
||||
#if AF_CAN_INCLUDE_SESSION_TASK_METRICS
|
||||
- (void)setTaskDidFinishCollectingMetricsBlock:(nullable void (^)(NSURLSession *session, NSURLSessionTask *task, NSURLSessionTaskMetrics * _Nullable metrics))block AF_API_AVAILABLE(ios(10), macosx(10.12), watchos(3), tvos(10));
|
||||
#endif
|
||||
///-------------------------------------------
|
||||
/// @name Setting Data Task Delegate Callbacks
|
||||
///-------------------------------------------
|
||||
@ -414,7 +420,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@param block A block object to be executed once all messages enqueued for a session have been delivered. The block has no return value and takes a single argument: the session.
|
||||
*/
|
||||
- (void)setDidFinishEventsForBackgroundURLSessionBlock:(nullable void (^)(NSURLSession *session))block;
|
||||
- (void)setDidFinishEventsForBackgroundURLSessionBlock:(nullable void (^)(NSURLSession *session))block AF_API_UNAVAILABLE(macos);
|
||||
|
||||
///-----------------------------------------------
|
||||
/// @name Setting Download Task Delegate Callbacks
|
||||
@ -428,7 +434,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
- (void)setDownloadTaskDidFinishDownloadingBlock:(nullable NSURL * _Nullable (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, NSURL *location))block;
|
||||
|
||||
/**
|
||||
Sets a block to be executed periodically to track download progress, as handled by the `NSURLSessionDownloadDelegate` method `URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesWritten:totalBytesExpectedToWrite:`.
|
||||
Sets a block to be executed periodically to track download progress, as handled by the `NSURLSessionDownloadDelegate` method `URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:`.
|
||||
|
||||
@param block A block object to be called when an undetermined number of bytes have been downloaded from the server. This block has no return value and takes five arguments: the session, the download task, the number of bytes read since the last time the download progress block was called, the total bytes read, and the total bytes expected to be read during the request, as initially determined by the expected content size of the `NSHTTPURLResponse` object. This block may be called multiple times, and will execute on the session manager operation queue.
|
||||
*/
|
||||
@ -467,6 +473,11 @@ FOUNDATION_EXPORT NSString * const AFNetworkingTaskDidSuspendNotification;
|
||||
*/
|
||||
FOUNDATION_EXPORT NSString * const AFURLSessionDidInvalidateNotification;
|
||||
|
||||
/**
|
||||
Posted when a session download task finished moving the temporary download file to a specified destination successfully.
|
||||
*/
|
||||
FOUNDATION_EXPORT NSString * const AFURLSessionDownloadTaskDidMoveFileSuccessfullyNotification;
|
||||
|
||||
/**
|
||||
Posted when a session download task encountered an error when moving the temporary download file to a specified destination.
|
||||
*/
|
||||
@ -497,4 +508,9 @@ FOUNDATION_EXPORT NSString * const AFNetworkingTaskDidCompleteAssetPathKey;
|
||||
*/
|
||||
FOUNDATION_EXPORT NSString * const AFNetworkingTaskDidCompleteErrorKey;
|
||||
|
||||
/**
|
||||
The session task metrics taken from the download task. Included in the userInfo dictionary of the `AFNetworkingTaskDidCompleteSessionTaskMetrics`
|
||||
*/
|
||||
FOUNDATION_EXPORT NSString * const AFNetworkingTaskDidCompleteSessionTaskMetrics;
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@ -22,33 +22,6 @@
|
||||
#import "AFURLSessionManager.h"
|
||||
#import <objc/runtime.h>
|
||||
|
||||
#ifndef NSFoundationVersionNumber_iOS_8_0
|
||||
#define NSFoundationVersionNumber_With_Fixed_5871104061079552_bug 1140.11
|
||||
#else
|
||||
#define NSFoundationVersionNumber_With_Fixed_5871104061079552_bug NSFoundationVersionNumber_iOS_8_0
|
||||
#endif
|
||||
|
||||
static dispatch_queue_t url_session_manager_creation_queue() {
|
||||
static dispatch_queue_t af_url_session_manager_creation_queue;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
af_url_session_manager_creation_queue = dispatch_queue_create("com.alamofire.networking.session.manager.creation", DISPATCH_QUEUE_SERIAL);
|
||||
});
|
||||
|
||||
return af_url_session_manager_creation_queue;
|
||||
}
|
||||
|
||||
static void url_session_manager_create_task_safely(dispatch_block_t block) {
|
||||
if (NSFoundationVersionNumber < NSFoundationVersionNumber_With_Fixed_5871104061079552_bug) {
|
||||
// Fix of bug
|
||||
// Open Radar:http://openradar.appspot.com/radar?id=5871104061079552 (status: Fixed in iOS8)
|
||||
// Issue about:https://github.com/AFNetworking/AFNetworking/issues/2093
|
||||
dispatch_sync(url_session_manager_creation_queue(), block);
|
||||
} else {
|
||||
block();
|
||||
}
|
||||
}
|
||||
|
||||
static dispatch_queue_t url_session_manager_processing_queue() {
|
||||
static dispatch_queue_t af_url_session_manager_processing_queue;
|
||||
static dispatch_once_t onceToken;
|
||||
@ -73,6 +46,7 @@ NSString * const AFNetworkingTaskDidResumeNotification = @"com.alamofire.network
|
||||
NSString * const AFNetworkingTaskDidCompleteNotification = @"com.alamofire.networking.task.complete";
|
||||
NSString * const AFNetworkingTaskDidSuspendNotification = @"com.alamofire.networking.task.suspend";
|
||||
NSString * const AFURLSessionDidInvalidateNotification = @"com.alamofire.networking.session.invalidate";
|
||||
NSString * const AFURLSessionDownloadTaskDidMoveFileSuccessfullyNotification = @"com.alamofire.networking.session.download.file-manager-succeed";
|
||||
NSString * const AFURLSessionDownloadTaskDidFailToMoveFileNotification = @"com.alamofire.networking.session.download.file-manager-error";
|
||||
|
||||
NSString * const AFNetworkingTaskDidCompleteSerializedResponseKey = @"com.alamofire.networking.task.complete.serializedresponse";
|
||||
@ -80,23 +54,24 @@ NSString * const AFNetworkingTaskDidCompleteResponseSerializerKey = @"com.alamof
|
||||
NSString * const AFNetworkingTaskDidCompleteResponseDataKey = @"com.alamofire.networking.complete.finish.responsedata";
|
||||
NSString * const AFNetworkingTaskDidCompleteErrorKey = @"com.alamofire.networking.task.complete.error";
|
||||
NSString * const AFNetworkingTaskDidCompleteAssetPathKey = @"com.alamofire.networking.task.complete.assetpath";
|
||||
NSString * const AFNetworkingTaskDidCompleteSessionTaskMetrics = @"com.alamofire.networking.complete.sessiontaskmetrics";
|
||||
|
||||
static NSString * const AFURLSessionManagerLockName = @"com.alamofire.networking.session.manager.lock";
|
||||
|
||||
static NSUInteger const AFMaximumNumberOfAttemptsToRecreateBackgroundSessionUploadTask = 3;
|
||||
|
||||
static void * AFTaskStateChangedContext = &AFTaskStateChangedContext;
|
||||
|
||||
typedef void (^AFURLSessionDidBecomeInvalidBlock)(NSURLSession *session, NSError *error);
|
||||
typedef NSURLSessionAuthChallengeDisposition (^AFURLSessionDidReceiveAuthenticationChallengeBlock)(NSURLSession *session, NSURLAuthenticationChallenge *challenge, NSURLCredential * __autoreleasing *credential);
|
||||
|
||||
typedef NSURLRequest * (^AFURLSessionTaskWillPerformHTTPRedirectionBlock)(NSURLSession *session, NSURLSessionTask *task, NSURLResponse *response, NSURLRequest *request);
|
||||
typedef NSURLSessionAuthChallengeDisposition (^AFURLSessionTaskDidReceiveAuthenticationChallengeBlock)(NSURLSession *session, NSURLSessionTask *task, NSURLAuthenticationChallenge *challenge, NSURLCredential * __autoreleasing *credential);
|
||||
typedef id (^AFURLSessionTaskAuthenticationChallengeBlock)(NSURLSession *session, NSURLSessionTask *task, NSURLAuthenticationChallenge *challenge, void (^completionHandler)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential));
|
||||
typedef void (^AFURLSessionDidFinishEventsForBackgroundURLSessionBlock)(NSURLSession *session);
|
||||
|
||||
typedef NSInputStream * (^AFURLSessionTaskNeedNewBodyStreamBlock)(NSURLSession *session, NSURLSessionTask *task);
|
||||
typedef void (^AFURLSessionTaskDidSendBodyDataBlock)(NSURLSession *session, NSURLSessionTask *task, int64_t bytesSent, int64_t totalBytesSent, int64_t totalBytesExpectedToSend);
|
||||
typedef void (^AFURLSessionTaskDidCompleteBlock)(NSURLSession *session, NSURLSessionTask *task, NSError *error);
|
||||
#if AF_CAN_INCLUDE_SESSION_TASK_METRICS
|
||||
typedef void (^AFURLSessionTaskDidFinishCollectingMetricsBlock)(NSURLSession *session, NSURLSessionTask *task, NSURLSessionTaskMetrics * metrics) AF_API_AVAILABLE(ios(10), macosx(10.12), watchos(3), tvos(10));
|
||||
#endif
|
||||
|
||||
typedef NSURLSessionResponseDisposition (^AFURLSessionDataTaskDidReceiveResponseBlock)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLResponse *response);
|
||||
typedef void (^AFURLSessionDataTaskDidBecomeDownloadTaskBlock)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLSessionDownloadTask *downloadTask);
|
||||
@ -110,15 +85,18 @@ typedef void (^AFURLSessionTaskProgressBlock)(NSProgress *);
|
||||
|
||||
typedef void (^AFURLSessionTaskCompletionHandler)(NSURLResponse *response, id responseObject, NSError *error);
|
||||
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@interface AFURLSessionManagerTaskDelegate : NSObject <NSURLSessionTaskDelegate, NSURLSessionDataDelegate, NSURLSessionDownloadDelegate>
|
||||
- (instancetype)initWithTask:(NSURLSessionTask *)task;
|
||||
@property (nonatomic, weak) AFURLSessionManager *manager;
|
||||
@property (nonatomic, strong) NSMutableData *mutableData;
|
||||
@property (nonatomic, strong) NSProgress *uploadProgress;
|
||||
@property (nonatomic, strong) NSProgress *downloadProgress;
|
||||
@property (nonatomic, copy) NSURL *downloadFileURL;
|
||||
#if AF_CAN_INCLUDE_SESSION_TASK_METRICS
|
||||
@property (nonatomic, strong) NSURLSessionTaskMetrics *sessionTaskMetrics AF_API_AVAILABLE(ios(10), macosx(10.12), watchos(3), tvos(10));
|
||||
#endif
|
||||
@property (nonatomic, copy) AFURLSessionDownloadTaskDidFinishDownloadingBlock downloadTaskDidFinishDownloading;
|
||||
@property (nonatomic, copy) AFURLSessionTaskProgressBlock uploadProgressBlock;
|
||||
@property (nonatomic, copy) AFURLSessionTaskProgressBlock downloadProgressBlock;
|
||||
@ -127,113 +105,56 @@ typedef void (^AFURLSessionTaskCompletionHandler)(NSURLResponse *response, id re
|
||||
|
||||
@implementation AFURLSessionManagerTaskDelegate
|
||||
|
||||
- (instancetype)init {
|
||||
- (instancetype)initWithTask:(NSURLSessionTask *)task {
|
||||
self = [super init];
|
||||
if (!self) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
self.mutableData = [NSMutableData data];
|
||||
self.uploadProgress = [[NSProgress alloc] initWithParent:nil userInfo:nil];
|
||||
self.uploadProgress.totalUnitCount = NSURLSessionTransferSizeUnknown;
|
||||
|
||||
self.downloadProgress = [[NSProgress alloc] initWithParent:nil userInfo:nil];
|
||||
self.downloadProgress.totalUnitCount = NSURLSessionTransferSizeUnknown;
|
||||
|
||||
_mutableData = [NSMutableData data];
|
||||
_uploadProgress = [[NSProgress alloc] initWithParent:nil userInfo:nil];
|
||||
_downloadProgress = [[NSProgress alloc] initWithParent:nil userInfo:nil];
|
||||
|
||||
__weak __typeof__(task) weakTask = task;
|
||||
for (NSProgress *progress in @[ _uploadProgress, _downloadProgress ])
|
||||
{
|
||||
progress.totalUnitCount = NSURLSessionTransferSizeUnknown;
|
||||
progress.cancellable = YES;
|
||||
progress.cancellationHandler = ^{
|
||||
[weakTask cancel];
|
||||
};
|
||||
progress.pausable = YES;
|
||||
progress.pausingHandler = ^{
|
||||
[weakTask suspend];
|
||||
};
|
||||
#if AF_CAN_USE_AT_AVAILABLE
|
||||
if (@available(macOS 10.11, *))
|
||||
#else
|
||||
if ([progress respondsToSelector:@selector(setResumingHandler:)])
|
||||
#endif
|
||||
{
|
||||
progress.resumingHandler = ^{
|
||||
[weakTask resume];
|
||||
};
|
||||
}
|
||||
|
||||
[progress addObserver:self
|
||||
forKeyPath:NSStringFromSelector(@selector(fractionCompleted))
|
||||
options:NSKeyValueObservingOptionNew
|
||||
context:NULL];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark - NSProgress Tracking
|
||||
|
||||
- (void)setupProgressForTask:(NSURLSessionTask *)task {
|
||||
__weak __typeof__(task) weakTask = task;
|
||||
|
||||
self.uploadProgress.totalUnitCount = task.countOfBytesExpectedToSend;
|
||||
self.downloadProgress.totalUnitCount = task.countOfBytesExpectedToReceive;
|
||||
[self.uploadProgress setCancellable:YES];
|
||||
[self.uploadProgress setCancellationHandler:^{
|
||||
__typeof__(weakTask) strongTask = weakTask;
|
||||
[strongTask cancel];
|
||||
}];
|
||||
[self.uploadProgress setPausable:YES];
|
||||
[self.uploadProgress setPausingHandler:^{
|
||||
__typeof__(weakTask) strongTask = weakTask;
|
||||
[strongTask suspend];
|
||||
}];
|
||||
if ([self.uploadProgress respondsToSelector:@selector(setResumingHandler:)]) {
|
||||
[self.uploadProgress setResumingHandler:^{
|
||||
__typeof__(weakTask) strongTask = weakTask;
|
||||
[strongTask resume];
|
||||
}];
|
||||
}
|
||||
|
||||
[self.downloadProgress setCancellable:YES];
|
||||
[self.downloadProgress setCancellationHandler:^{
|
||||
__typeof__(weakTask) strongTask = weakTask;
|
||||
[strongTask cancel];
|
||||
}];
|
||||
[self.downloadProgress setPausable:YES];
|
||||
[self.downloadProgress setPausingHandler:^{
|
||||
__typeof__(weakTask) strongTask = weakTask;
|
||||
[strongTask suspend];
|
||||
}];
|
||||
|
||||
if ([self.downloadProgress respondsToSelector:@selector(setResumingHandler:)]) {
|
||||
[self.downloadProgress setResumingHandler:^{
|
||||
__typeof__(weakTask) strongTask = weakTask;
|
||||
[strongTask resume];
|
||||
}];
|
||||
}
|
||||
|
||||
[task addObserver:self
|
||||
forKeyPath:NSStringFromSelector(@selector(countOfBytesReceived))
|
||||
options:NSKeyValueObservingOptionNew
|
||||
context:NULL];
|
||||
[task addObserver:self
|
||||
forKeyPath:NSStringFromSelector(@selector(countOfBytesExpectedToReceive))
|
||||
options:NSKeyValueObservingOptionNew
|
||||
context:NULL];
|
||||
|
||||
[task addObserver:self
|
||||
forKeyPath:NSStringFromSelector(@selector(countOfBytesSent))
|
||||
options:NSKeyValueObservingOptionNew
|
||||
context:NULL];
|
||||
[task addObserver:self
|
||||
forKeyPath:NSStringFromSelector(@selector(countOfBytesExpectedToSend))
|
||||
options:NSKeyValueObservingOptionNew
|
||||
context:NULL];
|
||||
|
||||
[self.downloadProgress addObserver:self
|
||||
forKeyPath:NSStringFromSelector(@selector(fractionCompleted))
|
||||
options:NSKeyValueObservingOptionNew
|
||||
context:NULL];
|
||||
[self.uploadProgress addObserver:self
|
||||
forKeyPath:NSStringFromSelector(@selector(fractionCompleted))
|
||||
options:NSKeyValueObservingOptionNew
|
||||
context:NULL];
|
||||
}
|
||||
|
||||
- (void)cleanUpProgressForTask:(NSURLSessionTask *)task {
|
||||
[task removeObserver:self forKeyPath:NSStringFromSelector(@selector(countOfBytesReceived))];
|
||||
[task removeObserver:self forKeyPath:NSStringFromSelector(@selector(countOfBytesExpectedToReceive))];
|
||||
[task removeObserver:self forKeyPath:NSStringFromSelector(@selector(countOfBytesSent))];
|
||||
[task removeObserver:self forKeyPath:NSStringFromSelector(@selector(countOfBytesExpectedToSend))];
|
||||
- (void)dealloc {
|
||||
[self.downloadProgress removeObserver:self forKeyPath:NSStringFromSelector(@selector(fractionCompleted))];
|
||||
[self.uploadProgress removeObserver:self forKeyPath:NSStringFromSelector(@selector(fractionCompleted))];
|
||||
}
|
||||
|
||||
#pragma mark - NSProgress Tracking
|
||||
|
||||
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
|
||||
if ([object isKindOfClass:[NSURLSessionTask class]] || [object isKindOfClass:[NSURLSessionDownloadTask class]]) {
|
||||
if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesReceived))]) {
|
||||
self.downloadProgress.completedUnitCount = [change[NSKeyValueChangeNewKey] longLongValue];
|
||||
} else if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesExpectedToReceive))]) {
|
||||
self.downloadProgress.totalUnitCount = [change[NSKeyValueChangeNewKey] longLongValue];
|
||||
} else if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesSent))]) {
|
||||
self.uploadProgress.completedUnitCount = [change[NSKeyValueChangeNewKey] longLongValue];
|
||||
} else if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesExpectedToSend))]) {
|
||||
self.uploadProgress.totalUnitCount = [change[NSKeyValueChangeNewKey] longLongValue];
|
||||
}
|
||||
}
|
||||
else if ([object isEqual:self.downloadProgress]) {
|
||||
if ([object isEqual:self.downloadProgress]) {
|
||||
if (self.downloadProgressBlock) {
|
||||
self.downloadProgressBlock(object);
|
||||
}
|
||||
@ -245,19 +166,20 @@ typedef void (^AFURLSessionTaskCompletionHandler)(NSURLResponse *response, id re
|
||||
}
|
||||
}
|
||||
|
||||
static const void * const AuthenticationChallengeErrorKey = &AuthenticationChallengeErrorKey;
|
||||
|
||||
#pragma mark - NSURLSessionTaskDelegate
|
||||
|
||||
- (void)URLSession:(__unused NSURLSession *)session
|
||||
task:(NSURLSessionTask *)task
|
||||
didCompleteWithError:(NSError *)error
|
||||
{
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wgnu"
|
||||
error = objc_getAssociatedObject(task, AuthenticationChallengeErrorKey) ?: error;
|
||||
__strong AFURLSessionManager *manager = self.manager;
|
||||
|
||||
__block id responseObject = nil;
|
||||
|
||||
__block NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
|
||||
NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
|
||||
userInfo[AFNetworkingTaskDidCompleteResponseSerializerKey] = manager.responseSerializer;
|
||||
|
||||
//Performance Improvement from #2672
|
||||
@ -268,6 +190,14 @@ didCompleteWithError:(NSError *)error
|
||||
self.mutableData = nil;
|
||||
}
|
||||
|
||||
#if AF_CAN_USE_AT_AVAILABLE && AF_CAN_INCLUDE_SESSION_TASK_METRICS
|
||||
if (@available(iOS 10, macOS 10.12, watchOS 3, tvOS 10, *)) {
|
||||
if (self.sessionTaskMetrics) {
|
||||
userInfo[AFNetworkingTaskDidCompleteSessionTaskMetrics] = self.sessionTaskMetrics;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (self.downloadFileURL) {
|
||||
userInfo[AFNetworkingTaskDidCompleteAssetPathKey] = self.downloadFileURL;
|
||||
} else if (data) {
|
||||
@ -314,34 +244,71 @@ didCompleteWithError:(NSError *)error
|
||||
});
|
||||
});
|
||||
}
|
||||
#pragma clang diagnostic pop
|
||||
}
|
||||
|
||||
#pragma mark - NSURLSessionDataTaskDelegate
|
||||
#if AF_CAN_INCLUDE_SESSION_TASK_METRICS
|
||||
- (void)URLSession:(NSURLSession *)session
|
||||
task:(NSURLSessionTask *)task
|
||||
didFinishCollectingMetrics:(NSURLSessionTaskMetrics *)metrics AF_API_AVAILABLE(ios(10), macosx(10.12), watchos(3), tvos(10)) {
|
||||
self.sessionTaskMetrics = metrics;
|
||||
}
|
||||
#endif
|
||||
|
||||
#pragma mark - NSURLSessionDataDelegate
|
||||
|
||||
- (void)URLSession:(__unused NSURLSession *)session
|
||||
dataTask:(__unused NSURLSessionDataTask *)dataTask
|
||||
didReceiveData:(NSData *)data
|
||||
{
|
||||
self.downloadProgress.totalUnitCount = dataTask.countOfBytesExpectedToReceive;
|
||||
self.downloadProgress.completedUnitCount = dataTask.countOfBytesReceived;
|
||||
|
||||
[self.mutableData appendData:data];
|
||||
}
|
||||
|
||||
#pragma mark - NSURLSessionDownloadTaskDelegate
|
||||
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
|
||||
didSendBodyData:(int64_t)bytesSent
|
||||
totalBytesSent:(int64_t)totalBytesSent
|
||||
totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend{
|
||||
|
||||
self.uploadProgress.totalUnitCount = task.countOfBytesExpectedToSend;
|
||||
self.uploadProgress.completedUnitCount = task.countOfBytesSent;
|
||||
}
|
||||
|
||||
#pragma mark - NSURLSessionDownloadDelegate
|
||||
|
||||
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
|
||||
didWriteData:(int64_t)bytesWritten
|
||||
totalBytesWritten:(int64_t)totalBytesWritten
|
||||
totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite{
|
||||
|
||||
self.downloadProgress.totalUnitCount = totalBytesExpectedToWrite;
|
||||
self.downloadProgress.completedUnitCount = totalBytesWritten;
|
||||
}
|
||||
|
||||
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
|
||||
didResumeAtOffset:(int64_t)fileOffset
|
||||
expectedTotalBytes:(int64_t)expectedTotalBytes{
|
||||
|
||||
self.downloadProgress.totalUnitCount = expectedTotalBytes;
|
||||
self.downloadProgress.completedUnitCount = fileOffset;
|
||||
}
|
||||
|
||||
- (void)URLSession:(NSURLSession *)session
|
||||
downloadTask:(NSURLSessionDownloadTask *)downloadTask
|
||||
didFinishDownloadingToURL:(NSURL *)location
|
||||
{
|
||||
NSError *fileManagerError = nil;
|
||||
self.downloadFileURL = nil;
|
||||
|
||||
if (self.downloadTaskDidFinishDownloading) {
|
||||
self.downloadFileURL = self.downloadTaskDidFinishDownloading(session, downloadTask, location);
|
||||
if (self.downloadFileURL) {
|
||||
[[NSFileManager defaultManager] moveItemAtURL:location toURL:self.downloadFileURL error:&fileManagerError];
|
||||
NSError *fileManagerError = nil;
|
||||
|
||||
if (fileManagerError) {
|
||||
if (![[NSFileManager defaultManager] moveItemAtURL:location toURL:self.downloadFileURL error:&fileManagerError]) {
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDownloadTaskDidFailToMoveFileNotification object:downloadTask userInfo:fileManagerError.userInfo];
|
||||
} else {
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDownloadTaskDidMoveFileSuccessfullyNotification object:downloadTask userInfo:nil];
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -413,7 +380,7 @@ static NSString * const AFNSURLSessionTaskDidSuspendNotification = @"com.alamofi
|
||||
8) Set the current class to the super class, and repeat steps 3-8
|
||||
*/
|
||||
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration ephemeralSessionConfiguration];
|
||||
NSURLSession * session = [NSURLSession sessionWithConfiguration:configuration];
|
||||
NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration];
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wnonnull"
|
||||
NSURLSessionDataTask *localDataTask = [session dataTaskWithURL:nil];
|
||||
@ -487,12 +454,15 @@ static NSString * const AFNSURLSessionTaskDidSuspendNotification = @"com.alamofi
|
||||
@property (readwrite, nonatomic, strong) NSLock *lock;
|
||||
@property (readwrite, nonatomic, copy) AFURLSessionDidBecomeInvalidBlock sessionDidBecomeInvalid;
|
||||
@property (readwrite, nonatomic, copy) AFURLSessionDidReceiveAuthenticationChallengeBlock sessionDidReceiveAuthenticationChallenge;
|
||||
@property (readwrite, nonatomic, copy) AFURLSessionDidFinishEventsForBackgroundURLSessionBlock didFinishEventsForBackgroundURLSession;
|
||||
@property (readwrite, nonatomic, copy) AFURLSessionDidFinishEventsForBackgroundURLSessionBlock didFinishEventsForBackgroundURLSession AF_API_UNAVAILABLE(macos);
|
||||
@property (readwrite, nonatomic, copy) AFURLSessionTaskWillPerformHTTPRedirectionBlock taskWillPerformHTTPRedirection;
|
||||
@property (readwrite, nonatomic, copy) AFURLSessionTaskDidReceiveAuthenticationChallengeBlock taskDidReceiveAuthenticationChallenge;
|
||||
@property (readwrite, nonatomic, copy) AFURLSessionTaskAuthenticationChallengeBlock authenticationChallengeHandler;
|
||||
@property (readwrite, nonatomic, copy) AFURLSessionTaskNeedNewBodyStreamBlock taskNeedNewBodyStream;
|
||||
@property (readwrite, nonatomic, copy) AFURLSessionTaskDidSendBodyDataBlock taskDidSendBodyData;
|
||||
@property (readwrite, nonatomic, copy) AFURLSessionTaskDidCompleteBlock taskDidComplete;
|
||||
#if AF_CAN_INCLUDE_SESSION_TASK_METRICS
|
||||
@property (readwrite, nonatomic, copy) AFURLSessionTaskDidFinishCollectingMetricsBlock taskDidFinishCollectingMetrics AF_API_AVAILABLE(ios(10), macosx(10.12), watchos(3), tvos(10));
|
||||
#endif
|
||||
@property (readwrite, nonatomic, copy) AFURLSessionDataTaskDidReceiveResponseBlock dataTaskDidReceiveResponse;
|
||||
@property (readwrite, nonatomic, copy) AFURLSessionDataTaskDidBecomeDownloadTaskBlock dataTaskDidBecomeDownloadTask;
|
||||
@property (readwrite, nonatomic, copy) AFURLSessionDataTaskDidReceiveDataBlock dataTaskDidReceiveData;
|
||||
@ -523,8 +493,6 @@ static NSString * const AFNSURLSessionTaskDidSuspendNotification = @"com.alamofi
|
||||
self.operationQueue = [[NSOperationQueue alloc] init];
|
||||
self.operationQueue.maxConcurrentOperationCount = 1;
|
||||
|
||||
self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue];
|
||||
|
||||
self.responseSerializer = [AFJSONResponseSerializer serializer];
|
||||
|
||||
self.securityPolicy = [AFSecurityPolicy defaultPolicy];
|
||||
@ -561,6 +529,19 @@ static NSString * const AFNSURLSessionTaskDidSuspendNotification = @"com.alamofi
|
||||
|
||||
#pragma mark -
|
||||
|
||||
- (NSURLSession *)session {
|
||||
|
||||
@synchronized (self) {
|
||||
if (!_session) {
|
||||
_session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue];
|
||||
}
|
||||
}
|
||||
return _session;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
|
||||
- (NSString *)taskDescriptionForSessionTasks {
|
||||
return [NSString stringWithFormat:@"%p", self];
|
||||
}
|
||||
@ -608,7 +589,6 @@ static NSString * const AFNSURLSessionTaskDidSuspendNotification = @"com.alamofi
|
||||
|
||||
[self.lock lock];
|
||||
self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;
|
||||
[delegate setupProgressForTask:task];
|
||||
[self addNotificationObserverForTask:task];
|
||||
[self.lock unlock];
|
||||
}
|
||||
@ -618,7 +598,7 @@ static NSString * const AFNSURLSessionTaskDidSuspendNotification = @"com.alamofi
|
||||
downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgressBlock
|
||||
completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
|
||||
{
|
||||
AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] init];
|
||||
AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] initWithTask:dataTask];
|
||||
delegate.manager = self;
|
||||
delegate.completionHandler = completionHandler;
|
||||
|
||||
@ -633,7 +613,7 @@ static NSString * const AFNSURLSessionTaskDidSuspendNotification = @"com.alamofi
|
||||
progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock
|
||||
completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
|
||||
{
|
||||
AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] init];
|
||||
AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] initWithTask:uploadTask];
|
||||
delegate.manager = self;
|
||||
delegate.completionHandler = completionHandler;
|
||||
|
||||
@ -649,7 +629,7 @@ static NSString * const AFNSURLSessionTaskDidSuspendNotification = @"com.alamofi
|
||||
destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination
|
||||
completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler
|
||||
{
|
||||
AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] init];
|
||||
AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] initWithTask:downloadTask];
|
||||
delegate.manager = self;
|
||||
delegate.completionHandler = completionHandler;
|
||||
|
||||
@ -669,9 +649,7 @@ static NSString * const AFNSURLSessionTaskDidSuspendNotification = @"com.alamofi
|
||||
- (void)removeDelegateForTask:(NSURLSessionTask *)task {
|
||||
NSParameterAssert(task);
|
||||
|
||||
AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task];
|
||||
[self.lock lock];
|
||||
[delegate cleanUpProgressForTask:task];
|
||||
[self removeNotificationObserverForTask:task];
|
||||
[self.mutableTaskDelegatesKeyedByTaskIdentifier removeObjectForKey:@(task.taskIdentifier)];
|
||||
[self.lock unlock];
|
||||
@ -719,14 +697,15 @@ static NSString * const AFNSURLSessionTaskDidSuspendNotification = @"com.alamofi
|
||||
|
||||
#pragma mark -
|
||||
|
||||
- (void)invalidateSessionCancelingTasks:(BOOL)cancelPendingTasks {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (cancelPendingTasks) {
|
||||
[self.session invalidateAndCancel];
|
||||
} else {
|
||||
[self.session finishTasksAndInvalidate];
|
||||
}
|
||||
});
|
||||
- (void)invalidateSessionCancelingTasks:(BOOL)cancelPendingTasks resetSession:(BOOL)resetSession {
|
||||
if (cancelPendingTasks) {
|
||||
[self.session invalidateAndCancel];
|
||||
} else {
|
||||
[self.session finishTasksAndInvalidate];
|
||||
}
|
||||
if (resetSession) {
|
||||
self.session = nil;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
@ -750,21 +729,12 @@ static NSString * const AFNSURLSessionTaskDidSuspendNotification = @"com.alamofi
|
||||
|
||||
#pragma mark -
|
||||
|
||||
- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request
|
||||
completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
|
||||
{
|
||||
return [self dataTaskWithRequest:request uploadProgress:nil downloadProgress:nil completionHandler:completionHandler];
|
||||
}
|
||||
|
||||
- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request
|
||||
uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgressBlock
|
||||
downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgressBlock
|
||||
completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError * _Nullable error))completionHandler {
|
||||
|
||||
__block NSURLSessionDataTask *dataTask = nil;
|
||||
url_session_manager_create_task_safely(^{
|
||||
dataTask = [self.session dataTaskWithRequest:request];
|
||||
});
|
||||
NSURLSessionDataTask *dataTask = [self.session dataTaskWithRequest:request];
|
||||
|
||||
[self addDelegateForDataTask:dataTask uploadProgress:uploadProgressBlock downloadProgress:downloadProgressBlock completionHandler:completionHandler];
|
||||
|
||||
@ -778,19 +748,14 @@ static NSString * const AFNSURLSessionTaskDidSuspendNotification = @"com.alamofi
|
||||
progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock
|
||||
completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
|
||||
{
|
||||
__block NSURLSessionUploadTask *uploadTask = nil;
|
||||
url_session_manager_create_task_safely(^{
|
||||
uploadTask = [self.session uploadTaskWithRequest:request fromFile:fileURL];
|
||||
});
|
||||
|
||||
if (!uploadTask && self.attemptsToRecreateUploadTasksForBackgroundSessions && self.session.configuration.identifier) {
|
||||
for (NSUInteger attempts = 0; !uploadTask && attempts < AFMaximumNumberOfAttemptsToRecreateBackgroundSessionUploadTask; attempts++) {
|
||||
uploadTask = [self.session uploadTaskWithRequest:request fromFile:fileURL];
|
||||
}
|
||||
NSURLSessionUploadTask *uploadTask = [self.session uploadTaskWithRequest:request fromFile:fileURL];
|
||||
|
||||
if (uploadTask) {
|
||||
[self addDelegateForUploadTask:uploadTask
|
||||
progress:uploadProgressBlock
|
||||
completionHandler:completionHandler];
|
||||
}
|
||||
|
||||
[self addDelegateForUploadTask:uploadTask progress:uploadProgressBlock completionHandler:completionHandler];
|
||||
|
||||
return uploadTask;
|
||||
}
|
||||
|
||||
@ -799,11 +764,8 @@ static NSString * const AFNSURLSessionTaskDidSuspendNotification = @"com.alamofi
|
||||
progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock
|
||||
completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
|
||||
{
|
||||
__block NSURLSessionUploadTask *uploadTask = nil;
|
||||
url_session_manager_create_task_safely(^{
|
||||
uploadTask = [self.session uploadTaskWithRequest:request fromData:bodyData];
|
||||
});
|
||||
|
||||
NSURLSessionUploadTask *uploadTask = [self.session uploadTaskWithRequest:request fromData:bodyData];
|
||||
|
||||
[self addDelegateForUploadTask:uploadTask progress:uploadProgressBlock completionHandler:completionHandler];
|
||||
|
||||
return uploadTask;
|
||||
@ -813,10 +775,7 @@ static NSString * const AFNSURLSessionTaskDidSuspendNotification = @"com.alamofi
|
||||
progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock
|
||||
completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
|
||||
{
|
||||
__block NSURLSessionUploadTask *uploadTask = nil;
|
||||
url_session_manager_create_task_safely(^{
|
||||
uploadTask = [self.session uploadTaskWithStreamedRequest:request];
|
||||
});
|
||||
NSURLSessionUploadTask *uploadTask = [self.session uploadTaskWithStreamedRequest:request];
|
||||
|
||||
[self addDelegateForUploadTask:uploadTask progress:uploadProgressBlock completionHandler:completionHandler];
|
||||
|
||||
@ -830,11 +789,8 @@ static NSString * const AFNSURLSessionTaskDidSuspendNotification = @"com.alamofi
|
||||
destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination
|
||||
completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler
|
||||
{
|
||||
__block NSURLSessionDownloadTask *downloadTask = nil;
|
||||
url_session_manager_create_task_safely(^{
|
||||
downloadTask = [self.session downloadTaskWithRequest:request];
|
||||
});
|
||||
|
||||
NSURLSessionDownloadTask *downloadTask = [self.session downloadTaskWithRequest:request];
|
||||
|
||||
[self addDelegateForDownloadTask:downloadTask progress:downloadProgressBlock destination:destination completionHandler:completionHandler];
|
||||
|
||||
return downloadTask;
|
||||
@ -845,10 +801,7 @@ static NSString * const AFNSURLSessionTaskDidSuspendNotification = @"com.alamofi
|
||||
destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination
|
||||
completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler
|
||||
{
|
||||
__block NSURLSessionDownloadTask *downloadTask = nil;
|
||||
url_session_manager_create_task_safely(^{
|
||||
downloadTask = [self.session downloadTaskWithResumeData:resumeData];
|
||||
});
|
||||
NSURLSessionDownloadTask *downloadTask = [self.session downloadTaskWithResumeData:resumeData];
|
||||
|
||||
[self addDelegateForDownloadTask:downloadTask progress:downloadProgressBlock destination:destination completionHandler:completionHandler];
|
||||
|
||||
@ -874,9 +827,11 @@ static NSString * const AFNSURLSessionTaskDidSuspendNotification = @"com.alamofi
|
||||
self.sessionDidReceiveAuthenticationChallenge = block;
|
||||
}
|
||||
|
||||
#if !TARGET_OS_OSX
|
||||
- (void)setDidFinishEventsForBackgroundURLSessionBlock:(void (^)(NSURLSession *session))block {
|
||||
self.didFinishEventsForBackgroundURLSession = block;
|
||||
}
|
||||
#endif
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@ -888,10 +843,6 @@ static NSString * const AFNSURLSessionTaskDidSuspendNotification = @"com.alamofi
|
||||
self.taskWillPerformHTTPRedirection = block;
|
||||
}
|
||||
|
||||
- (void)setTaskDidReceiveAuthenticationChallengeBlock:(NSURLSessionAuthChallengeDisposition (^)(NSURLSession *session, NSURLSessionTask *task, NSURLAuthenticationChallenge *challenge, NSURLCredential * __autoreleasing *credential))block {
|
||||
self.taskDidReceiveAuthenticationChallenge = block;
|
||||
}
|
||||
|
||||
- (void)setTaskDidSendBodyDataBlock:(void (^)(NSURLSession *session, NSURLSessionTask *task, int64_t bytesSent, int64_t totalBytesSent, int64_t totalBytesExpectedToSend))block {
|
||||
self.taskDidSendBodyData = block;
|
||||
}
|
||||
@ -900,6 +851,12 @@ static NSString * const AFNSURLSessionTaskDidSuspendNotification = @"com.alamofi
|
||||
self.taskDidComplete = block;
|
||||
}
|
||||
|
||||
#if AF_CAN_INCLUDE_SESSION_TASK_METRICS
|
||||
- (void)setTaskDidFinishCollectingMetricsBlock:(void (^)(NSURLSession * _Nonnull, NSURLSessionTask * _Nonnull, NSURLSessionTaskMetrics * _Nullable))block AF_API_AVAILABLE(ios(10), macosx(10.12), watchos(3), tvos(10)) {
|
||||
self.taskDidFinishCollectingMetrics = block;
|
||||
}
|
||||
#endif
|
||||
|
||||
#pragma mark -
|
||||
|
||||
- (void)setDataTaskDidReceiveResponseBlock:(NSURLSessionResponseDisposition (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLResponse *response))block {
|
||||
@ -939,15 +896,20 @@ static NSString * const AFNSURLSessionTaskDidSuspendNotification = @"com.alamofi
|
||||
}
|
||||
|
||||
- (BOOL)respondsToSelector:(SEL)selector {
|
||||
if (selector == @selector(URLSession:task:willPerformHTTPRedirection:newRequest:completionHandler:)) {
|
||||
if (selector == @selector(URLSession:didReceiveChallenge:completionHandler:)) {
|
||||
return self.sessionDidReceiveAuthenticationChallenge != nil;
|
||||
} else if (selector == @selector(URLSession:task:willPerformHTTPRedirection:newRequest:completionHandler:)) {
|
||||
return self.taskWillPerformHTTPRedirection != nil;
|
||||
} else if (selector == @selector(URLSession:dataTask:didReceiveResponse:completionHandler:)) {
|
||||
return self.dataTaskDidReceiveResponse != nil;
|
||||
} else if (selector == @selector(URLSession:dataTask:willCacheResponse:completionHandler:)) {
|
||||
return self.dataTaskWillCacheResponse != nil;
|
||||
} else if (selector == @selector(URLSessionDidFinishEventsForBackgroundURLSession:)) {
|
||||
}
|
||||
#if !TARGET_OS_OSX
|
||||
else if (selector == @selector(URLSessionDidFinishEventsForBackgroundURLSession:)) {
|
||||
return self.didFinishEventsForBackgroundURLSession != nil;
|
||||
}
|
||||
#endif
|
||||
|
||||
return [[self class] instancesRespondToSelector:selector];
|
||||
}
|
||||
@ -968,27 +930,10 @@ didBecomeInvalidWithError:(NSError *)error
|
||||
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
|
||||
completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
|
||||
{
|
||||
NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
|
||||
__block NSURLCredential *credential = nil;
|
||||
NSAssert(self.sessionDidReceiveAuthenticationChallenge != nil, @"`respondsToSelector:` implementation forces `URLSession:didReceiveChallenge:completionHandler:` to be called only if `self.sessionDidReceiveAuthenticationChallenge` is not nil");
|
||||
|
||||
if (self.sessionDidReceiveAuthenticationChallenge) {
|
||||
disposition = self.sessionDidReceiveAuthenticationChallenge(session, challenge, &credential);
|
||||
} else {
|
||||
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
|
||||
if ([self.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {
|
||||
credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
|
||||
if (credential) {
|
||||
disposition = NSURLSessionAuthChallengeUseCredential;
|
||||
} else {
|
||||
disposition = NSURLSessionAuthChallengePerformDefaultHandling;
|
||||
}
|
||||
} else {
|
||||
disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
|
||||
}
|
||||
} else {
|
||||
disposition = NSURLSessionAuthChallengePerformDefaultHandling;
|
||||
}
|
||||
}
|
||||
NSURLCredential *credential = nil;
|
||||
NSURLSessionAuthChallengeDisposition disposition = self.sessionDidReceiveAuthenticationChallenge(session, challenge, &credential);
|
||||
|
||||
if (completionHandler) {
|
||||
completionHandler(disposition, credential);
|
||||
@ -1019,21 +964,40 @@ willPerformHTTPRedirection:(NSHTTPURLResponse *)response
|
||||
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
|
||||
completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
|
||||
{
|
||||
BOOL evaluateServerTrust = NO;
|
||||
NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
|
||||
__block NSURLCredential *credential = nil;
|
||||
NSURLCredential *credential = nil;
|
||||
|
||||
if (self.taskDidReceiveAuthenticationChallenge) {
|
||||
disposition = self.taskDidReceiveAuthenticationChallenge(session, task, challenge, &credential);
|
||||
} else {
|
||||
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
|
||||
if ([self.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {
|
||||
disposition = NSURLSessionAuthChallengeUseCredential;
|
||||
credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
|
||||
} else {
|
||||
disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
|
||||
}
|
||||
if (self.authenticationChallengeHandler) {
|
||||
id result = self.authenticationChallengeHandler(session, task, challenge, completionHandler);
|
||||
if (result == nil) {
|
||||
return;
|
||||
} else if ([result isKindOfClass:NSError.class]) {
|
||||
objc_setAssociatedObject(task, AuthenticationChallengeErrorKey, result, OBJC_ASSOCIATION_RETAIN);
|
||||
disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
|
||||
} else if ([result isKindOfClass:NSURLCredential.class]) {
|
||||
credential = result;
|
||||
disposition = NSURLSessionAuthChallengeUseCredential;
|
||||
} else if ([result isKindOfClass:NSNumber.class]) {
|
||||
disposition = [result integerValue];
|
||||
NSAssert(disposition == NSURLSessionAuthChallengePerformDefaultHandling || disposition == NSURLSessionAuthChallengeCancelAuthenticationChallenge || disposition == NSURLSessionAuthChallengeRejectProtectionSpace, @"");
|
||||
evaluateServerTrust = disposition == NSURLSessionAuthChallengePerformDefaultHandling && [challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust];
|
||||
} else {
|
||||
disposition = NSURLSessionAuthChallengePerformDefaultHandling;
|
||||
@throw [NSException exceptionWithName:@"Invalid Return Value" reason:@"The return value from the authentication challenge handler must be nil, an NSError, an NSURLCredential or an NSNumber." userInfo:nil];
|
||||
}
|
||||
} else {
|
||||
evaluateServerTrust = [challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust];
|
||||
}
|
||||
|
||||
if (evaluateServerTrust) {
|
||||
if ([self.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {
|
||||
disposition = NSURLSessionAuthChallengeUseCredential;
|
||||
credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
|
||||
} else {
|
||||
objc_setAssociatedObject(task, AuthenticationChallengeErrorKey,
|
||||
[self serverTrustErrorForServerTrust:challenge.protectionSpace.serverTrust url:task.currentRequest.URL],
|
||||
OBJC_ASSOCIATION_RETAIN);
|
||||
disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1042,6 +1006,31 @@ didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
|
||||
}
|
||||
}
|
||||
|
||||
- (nonnull NSError *)serverTrustErrorForServerTrust:(nullable SecTrustRef)serverTrust url:(nullable NSURL *)url
|
||||
{
|
||||
NSBundle *CFNetworkBundle = [NSBundle bundleWithIdentifier:@"com.apple.CFNetwork"];
|
||||
NSString *defaultValue = @"The certificate for this server is invalid. You might be connecting to a server that is pretending to be “%@” which could put your confidential information at risk.";
|
||||
NSString *descriptionFormat = NSLocalizedStringWithDefaultValue(@"Err-1202.w", nil, CFNetworkBundle, defaultValue, @"") ?: defaultValue;
|
||||
NSString *localizedDescription = [descriptionFormat componentsSeparatedByString:@"%@"].count <= 2 ? [NSString localizedStringWithFormat:descriptionFormat, url.host] : descriptionFormat;
|
||||
NSMutableDictionary *userInfo = [@{
|
||||
NSLocalizedDescriptionKey: localizedDescription
|
||||
} mutableCopy];
|
||||
|
||||
if (serverTrust) {
|
||||
userInfo[NSURLErrorFailingURLPeerTrustErrorKey] = (__bridge id)serverTrust;
|
||||
}
|
||||
|
||||
if (url) {
|
||||
userInfo[NSURLErrorFailingURLErrorKey] = url;
|
||||
|
||||
if (url.absoluteString) {
|
||||
userInfo[NSURLErrorFailingURLStringErrorKey] = url.absoluteString;
|
||||
}
|
||||
}
|
||||
|
||||
return [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorServerCertificateUntrusted userInfo:userInfo];
|
||||
}
|
||||
|
||||
- (void)URLSession:(NSURLSession *)session
|
||||
task:(NSURLSessionTask *)task
|
||||
needNewBodyStream:(void (^)(NSInputStream *bodyStream))completionHandler
|
||||
@ -1067,12 +1056,18 @@ totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend
|
||||
{
|
||||
|
||||
int64_t totalUnitCount = totalBytesExpectedToSend;
|
||||
if(totalUnitCount == NSURLSessionTransferSizeUnknown) {
|
||||
if (totalUnitCount == NSURLSessionTransferSizeUnknown) {
|
||||
NSString *contentLength = [task.originalRequest valueForHTTPHeaderField:@"Content-Length"];
|
||||
if(contentLength) {
|
||||
if (contentLength) {
|
||||
totalUnitCount = (int64_t) [contentLength longLongValue];
|
||||
}
|
||||
}
|
||||
|
||||
AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task];
|
||||
|
||||
if (delegate) {
|
||||
[delegate URLSession:session task:task didSendBodyData:bytesSent totalBytesSent:totalBytesSent totalBytesExpectedToSend:totalBytesExpectedToSend];
|
||||
}
|
||||
|
||||
if (self.taskDidSendBodyData) {
|
||||
self.taskDidSendBodyData(session, task, bytesSent, totalBytesSent, totalUnitCount);
|
||||
@ -1097,6 +1092,23 @@ didCompleteWithError:(NSError *)error
|
||||
}
|
||||
}
|
||||
|
||||
#if AF_CAN_INCLUDE_SESSION_TASK_METRICS
|
||||
- (void)URLSession:(NSURLSession *)session
|
||||
task:(NSURLSessionTask *)task
|
||||
didFinishCollectingMetrics:(NSURLSessionTaskMetrics *)metrics AF_API_AVAILABLE(ios(10), macosx(10.12), watchos(3), tvos(10))
|
||||
{
|
||||
AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task];
|
||||
// Metrics may fire after URLSession:task:didCompleteWithError: is called, delegate may be nil
|
||||
if (delegate) {
|
||||
[delegate URLSession:session task:task didFinishCollectingMetrics:metrics];
|
||||
}
|
||||
|
||||
if (self.taskDidFinishCollectingMetrics) {
|
||||
self.taskDidFinishCollectingMetrics(session, task, metrics);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#pragma mark - NSURLSessionDataDelegate
|
||||
|
||||
- (void)URLSession:(NSURLSession *)session
|
||||
@ -1159,6 +1171,7 @@ didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask
|
||||
}
|
||||
}
|
||||
|
||||
#if !TARGET_OS_OSX
|
||||
- (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session {
|
||||
if (self.didFinishEventsForBackgroundURLSession) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
@ -1166,6 +1179,7 @@ didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask
|
||||
});
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#pragma mark - NSURLSessionDownloadDelegate
|
||||
|
||||
@ -1179,9 +1193,11 @@ didFinishDownloadingToURL:(NSURL *)location
|
||||
if (fileURL) {
|
||||
delegate.downloadFileURL = fileURL;
|
||||
NSError *error = nil;
|
||||
[[NSFileManager defaultManager] moveItemAtURL:location toURL:fileURL error:&error];
|
||||
if (error) {
|
||||
|
||||
if (![[NSFileManager defaultManager] moveItemAtURL:location toURL:fileURL error:&error]) {
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDownloadTaskDidFailToMoveFileNotification object:downloadTask userInfo:error.userInfo];
|
||||
} else {
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDownloadTaskDidMoveFileSuccessfullyNotification object:downloadTask userInfo:nil];
|
||||
}
|
||||
|
||||
return;
|
||||
@ -1199,6 +1215,13 @@ didFinishDownloadingToURL:(NSURL *)location
|
||||
totalBytesWritten:(int64_t)totalBytesWritten
|
||||
totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
|
||||
{
|
||||
|
||||
AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:downloadTask];
|
||||
|
||||
if (delegate) {
|
||||
[delegate URLSession:session downloadTask:downloadTask didWriteData:bytesWritten totalBytesWritten:totalBytesWritten totalBytesExpectedToWrite:totalBytesExpectedToWrite];
|
||||
}
|
||||
|
||||
if (self.downloadTaskDidWriteData) {
|
||||
self.downloadTaskDidWriteData(session, downloadTask, bytesWritten, totalBytesWritten, totalBytesExpectedToWrite);
|
||||
}
|
||||
@ -1209,6 +1232,13 @@ totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
|
||||
didResumeAtOffset:(int64_t)fileOffset
|
||||
expectedTotalBytes:(int64_t)expectedTotalBytes
|
||||
{
|
||||
|
||||
AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:downloadTask];
|
||||
|
||||
if (delegate) {
|
||||
[delegate URLSession:session downloadTask:downloadTask didResumeAtOffset:fileOffset expectedTotalBytes:expectedTotalBytes];
|
||||
}
|
||||
|
||||
if (self.downloadTaskDidResume) {
|
||||
self.downloadTaskDidResume(session, downloadTask, fileOffset, expectedTotalBytes);
|
||||
}
|
||||
|
||||
2
Example/Pods/AFNetworking/LICENSE
generated
2
Example/Pods/AFNetworking/LICENSE
generated
@ -1,4 +1,4 @@
|
||||
Copyright (c) 2011–2016 Alamofire Software Foundation (http://alamofire.org/)
|
||||
Copyright (c) 2011-2020 Alamofire Software Foundation (http://alamofire.org/)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
60
Example/Pods/AFNetworking/README.md
generated
60
Example/Pods/AFNetworking/README.md
generated
@ -2,25 +2,20 @@
|
||||
<img src="https://raw.github.com/AFNetworking/AFNetworking/assets/afnetworking-logo.png" alt="AFNetworking" title="AFNetworking">
|
||||
</p>
|
||||
|
||||
[](https://travis-ci.org/AFNetworking/AFNetworking)
|
||||
[](https://codecov.io/github/AFNetworking/AFNetworking?branch=master)
|
||||
[](https://github.com/AFNetworking/AFNetworking/actions)
|
||||
[](https://img.shields.io/cocoapods/v/AFNetworking.svg)
|
||||
[](https://github.com/Carthage/Carthage)
|
||||
[](http://cocoadocs.org/docsets/AFNetworking)
|
||||
[](http://twitter.com/AFNetworking)
|
||||
|
||||
AFNetworking is a delightful networking library for iOS and Mac OS X. It's built on top of the [Foundation URL Loading System](http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/URLLoadingSystem/URLLoadingSystem.html), extending the powerful high-level networking abstractions built into Cocoa. It has a modular architecture with well-designed, feature-rich APIs that are a joy to use.
|
||||
AFNetworking is a delightful networking library for iOS, macOS, watchOS, and tvOS. It's built on top of the [Foundation URL Loading System](https://developer.apple.com/documentation/foundation/url_loading_system), extending the powerful high-level networking abstractions built into Cocoa. It has a modular architecture with well-designed, feature-rich APIs that are a joy to use.
|
||||
|
||||
Perhaps the most important feature of all, however, is the amazing community of developers who use and contribute to AFNetworking every day. AFNetworking powers some of the most popular and critically-acclaimed apps on the iPhone, iPad, and Mac.
|
||||
|
||||
Choose AFNetworking for your next project, or migrate over your existing projects—you'll be happy you did!
|
||||
|
||||
## How To Get Started
|
||||
|
||||
- [Download AFNetworking](https://github.com/AFNetworking/AFNetworking/archive/master.zip) and try out the included Mac and iPhone example apps
|
||||
- Read the ["Getting Started" guide](https://github.com/AFNetworking/AFNetworking/wiki/Getting-Started-with-AFNetworking), [FAQ](https://github.com/AFNetworking/AFNetworking/wiki/AFNetworking-FAQ), or [other articles on the Wiki](https://github.com/AFNetworking/AFNetworking/wiki)
|
||||
- Check out the [documentation](http://cocoadocs.org/docsets/AFNetworking/) for a comprehensive look at all of the APIs available in AFNetworking
|
||||
- Read the [AFNetworking 3.0 Migration Guide](https://github.com/AFNetworking/AFNetworking/wiki/AFNetworking-3.0-Migration-Guide) for an overview of the architectural changes from 2.0.
|
||||
|
||||
## Communication
|
||||
|
||||
@ -35,61 +30,44 @@ AFNetworking supports multiple methods for installing the library in a project.
|
||||
|
||||
## Installation with CocoaPods
|
||||
|
||||
[CocoaPods](http://cocoapods.org) is a dependency manager for Objective-C, which automates and simplifies the process of using 3rd-party libraries like AFNetworking in your projects. See the ["Getting Started" guide for more information](https://github.com/AFNetworking/AFNetworking/wiki/Getting-Started-with-AFNetworking). You can install it with the following command:
|
||||
|
||||
```bash
|
||||
$ gem install cocoapods
|
||||
```
|
||||
|
||||
> CocoaPods 0.39.0+ is required to build AFNetworking 3.0.0+.
|
||||
|
||||
#### Podfile
|
||||
|
||||
To integrate AFNetworking into your Xcode project using CocoaPods, specify it in your `Podfile`:
|
||||
|
||||
```ruby
|
||||
source 'https://github.com/CocoaPods/Specs.git'
|
||||
platform :ios, '8.0'
|
||||
|
||||
pod 'AFNetworking', '~> 3.0'
|
||||
pod 'AFNetworking', '~> 4.0'
|
||||
```
|
||||
|
||||
Then, run the following command:
|
||||
### Installation with Swift Package Manager
|
||||
|
||||
```bash
|
||||
$ pod install
|
||||
Once you have your Swift package set up, adding AFNetworking as a dependency is as easy as adding it to the `dependencies` value of your `Package.swift`.
|
||||
|
||||
```swift
|
||||
dependencies: [
|
||||
.package(url: "https://github.com/AFNetworking/AFNetworking.git", .upToNextMajor(from: "4.0.0"))
|
||||
]
|
||||
```
|
||||
|
||||
> Note: AFNetworking's Swift package does not include it's UIKit extensions.
|
||||
|
||||
### Installation with Carthage
|
||||
|
||||
[Carthage](https://github.com/Carthage/Carthage) is a decentralized dependency manager that builds your dependencies and provides you with binary frameworks.
|
||||
|
||||
You can install Carthage with [Homebrew](http://brew.sh/) using the following command:
|
||||
|
||||
```bash
|
||||
$ brew update
|
||||
$ brew install carthage
|
||||
```
|
||||
|
||||
To integrate AFNetworking into your Xcode project using Carthage, specify it in your `Cartfile`:
|
||||
[Carthage](https://github.com/Carthage/Carthage) is a decentralized dependency manager that builds your dependencies and provides you with binary frameworks. To integrate AFNetworking, add the following to your `Cartfile`.
|
||||
|
||||
```ogdl
|
||||
github "AFNetworking/AFNetworking" ~> 3.0
|
||||
github "AFNetworking/AFNetworking" ~> 4.0
|
||||
```
|
||||
|
||||
Run `carthage` to build the framework and drag the built `AFNetworking.framework` into your Xcode project.
|
||||
|
||||
## Requirements
|
||||
|
||||
| AFNetworking Version | Minimum iOS Target | Minimum OS X Target | Minimum watchOS Target | Minimum tvOS Target | Notes |
|
||||
| AFNetworking Version | Minimum iOS Target | Minimum macOS Target | Minimum watchOS Target | Minimum tvOS Target | Notes |
|
||||
|:--------------------:|:---------------------------:|:----------------------------:|:----------------------------:|:----------------------------:|:-------------------------------------------------------------------------:|
|
||||
| 4.x | iOS 9 | macOS 10.10 | watchOS 2.0 | tvOS 9.0 | Xcode 11+ is required. |
|
||||
| 3.x | iOS 7 | OS X 10.9 | watchOS 2.0 | tvOS 9.0 | Xcode 7+ is required. `NSURLConnectionOperation` support has been removed. |
|
||||
| 2.6 -> 2.6.3 | iOS 7 | OS X 10.9 | watchOS 2.0 | n/a | Xcode 7+ is required. |
|
||||
| 2.0 -> 2.5.4 | iOS 6 | OS X 10.8 | n/a | n/a | Xcode 5+ is required. `NSURLSession` subspec requires iOS 7 or OS X 10.9. |
|
||||
| 1.x | iOS 5 | Mac OS X 10.7 | n/a | n/a |
|
||||
| 0.10.x | iOS 4 | Mac OS X 10.6 | n/a | n/a |
|
||||
|
||||
(OS X projects must support [64-bit with modern Cocoa runtime](https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtVersionsPlatforms.html)).
|
||||
(macOS projects must support [64-bit with modern Cocoa runtime](https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtVersionsPlatforms.html)).
|
||||
|
||||
> Programming in Swift? Try [Alamofire](https://github.com/Alamofire/Alamofire) for a more conventional set of APIs.
|
||||
|
||||
@ -110,7 +88,7 @@ Run `carthage` to build the framework and drag the built `AFNetworking.framework
|
||||
- `AFHTTPResponseSerializer`
|
||||
- `AFJSONResponseSerializer`
|
||||
- `AFXMLParserResponseSerializer`
|
||||
- `AFXMLDocumentResponseSerializer` _(Mac OS X)_
|
||||
- `AFXMLDocumentResponseSerializer` _(macOS)_
|
||||
- `AFPropertyListResponseSerializer`
|
||||
- `AFImageResponseSerializer`
|
||||
- `AFCompoundResponseSerializer`
|
||||
@ -317,4 +295,4 @@ If you believe you have identified a security vulnerability with AFNetworking, y
|
||||
|
||||
## License
|
||||
|
||||
AFNetworking is released under the MIT license. See LICENSE for details.
|
||||
AFNetworking is released under the MIT license. See [LICENSE](https://github.com/AFNetworking/AFNetworking/blob/master/LICENSE) for details.
|
||||
|
||||
@ -72,6 +72,17 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
*/
|
||||
@protocol AFImageRequestCache <AFImageCache>
|
||||
|
||||
/**
|
||||
Asks if the image should be cached using an identifier created from the request and additional identifier.
|
||||
|
||||
@param image The image to be cached.
|
||||
@param request The unique URL request identifing the image asset.
|
||||
@param identifier The additional identifier to apply to the URL request to identify the image.
|
||||
|
||||
@return A BOOL indicating whether or not the image should be added to the cache. YES will cache, NO will prevent caching.
|
||||
*/
|
||||
- (BOOL)shouldCacheImage:(UIImage *)image forRequest:(NSURLRequest *)request withAdditionalIdentifier:(nullable NSString *)identifier;
|
||||
|
||||
/**
|
||||
Adds the image to the cache using an identifier created from the request and additional identifier.
|
||||
|
||||
|
||||
@ -28,7 +28,7 @@
|
||||
@interface AFCachedImage : NSObject
|
||||
|
||||
@property (nonatomic, strong) UIImage *image;
|
||||
@property (nonatomic, strong) NSString *identifier;
|
||||
@property (nonatomic, copy) NSString *identifier;
|
||||
@property (nonatomic, assign) UInt64 totalBytes;
|
||||
@property (nonatomic, strong) NSDate *lastAccessDate;
|
||||
@property (nonatomic, assign) UInt64 currentMemoryUsage;
|
||||
@ -37,7 +37,7 @@
|
||||
|
||||
@implementation AFCachedImage
|
||||
|
||||
-(instancetype)initWithImage:(UIImage *)image identifier:(NSString *)identifier {
|
||||
- (instancetype)initWithImage:(UIImage *)image identifier:(NSString *)identifier {
|
||||
if (self = [self init]) {
|
||||
self.image = image;
|
||||
self.identifier = identifier;
|
||||
@ -51,7 +51,7 @@
|
||||
return self;
|
||||
}
|
||||
|
||||
- (UIImage*)accessImage {
|
||||
- (UIImage *)accessImage {
|
||||
self.lastAccessDate = [NSDate date];
|
||||
return self.image;
|
||||
}
|
||||
@ -134,7 +134,7 @@
|
||||
[self.cachedImages removeObjectForKey:cachedImage.identifier];
|
||||
bytesPurged += cachedImage.totalBytes;
|
||||
if (bytesPurged >= bytesToPurge) {
|
||||
break ;
|
||||
break;
|
||||
}
|
||||
}
|
||||
self.currentMemoryUsage -= bytesPurged;
|
||||
@ -196,6 +196,10 @@
|
||||
return key;
|
||||
}
|
||||
|
||||
- (BOOL)shouldCacheImage:(UIImage *)image forRequest:(NSURLRequest *)request withAdditionalIdentifier:(nullable NSString *)identifier {
|
||||
return YES;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#endif
|
||||
|
||||
@ -67,7 +67,7 @@ typedef NS_ENUM(NSInteger, AFImageDownloadPrioritization) {
|
||||
/**
|
||||
Defines the order prioritization of incoming download requests being inserted into the queue. `AFImageDownloadPrioritizationFIFO` by default.
|
||||
*/
|
||||
@property (nonatomic, assign) AFImageDownloadPrioritization downloadPrioritizaton;
|
||||
@property (nonatomic, assign) AFImageDownloadPrioritization downloadPrioritization;
|
||||
|
||||
/**
|
||||
The shared default instance of `AFImageDownloader` initialized with default values.
|
||||
@ -81,6 +81,11 @@ typedef NS_ENUM(NSInteger, AFImageDownloadPrioritization) {
|
||||
*/
|
||||
+ (NSURLCache *)defaultURLCache;
|
||||
|
||||
/**
|
||||
The default `NSURLSessionConfiguration` with common usage parameter values.
|
||||
*/
|
||||
+ (NSURLSessionConfiguration *)defaultURLSessionConfiguration;
|
||||
|
||||
/**
|
||||
Default initializer
|
||||
|
||||
@ -88,6 +93,15 @@ typedef NS_ENUM(NSInteger, AFImageDownloadPrioritization) {
|
||||
*/
|
||||
- (instancetype)init;
|
||||
|
||||
/**
|
||||
Initializer with specific `URLSessionConfiguration`
|
||||
|
||||
@param configuration The `NSURLSessionConfiguration` to be be used
|
||||
|
||||
@return An instance of `AFImageDownloader` initialized with default values and custom `NSURLSessionConfiguration`
|
||||
*/
|
||||
- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration;
|
||||
|
||||
/**
|
||||
Initializes the `AFImageDownloader` instance with the given session manager, download prioritization, maximum active download count and image cache.
|
||||
|
||||
|
||||
@ -28,8 +28,8 @@
|
||||
|
||||
@interface AFImageDownloaderResponseHandler : NSObject
|
||||
@property (nonatomic, strong) NSUUID *uuid;
|
||||
@property (nonatomic, copy) void (^successBlock)(NSURLRequest*, NSHTTPURLResponse*, UIImage*);
|
||||
@property (nonatomic, copy) void (^failureBlock)(NSURLRequest*, NSHTTPURLResponse*, NSError*);
|
||||
@property (nonatomic, copy) void (^successBlock)(NSURLRequest *, NSHTTPURLResponse *, UIImage *);
|
||||
@property (nonatomic, copy) void (^failureBlock)(NSURLRequest *, NSHTTPURLResponse *, NSError *);
|
||||
@end
|
||||
|
||||
@implementation AFImageDownloaderResponseHandler
|
||||
@ -71,11 +71,11 @@
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)addResponseHandler:(AFImageDownloaderResponseHandler*)handler {
|
||||
- (void)addResponseHandler:(AFImageDownloaderResponseHandler *)handler {
|
||||
[self.responseHandlers addObject:handler];
|
||||
}
|
||||
|
||||
- (void)removeResponseHandler:(AFImageDownloaderResponseHandler*)handler {
|
||||
- (void)removeResponseHandler:(AFImageDownloaderResponseHandler *)handler {
|
||||
[self.responseHandlers removeObject:handler];
|
||||
}
|
||||
|
||||
@ -106,13 +106,27 @@
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation AFImageDownloader
|
||||
|
||||
+ (NSURLCache *)defaultURLCache {
|
||||
return [[NSURLCache alloc] initWithMemoryCapacity:20 * 1024 * 1024
|
||||
diskCapacity:150 * 1024 * 1024
|
||||
diskPath:@"com.alamofire.imagedownloader"];
|
||||
NSUInteger memoryCapacity = 20 * 1024 * 1024; // 20MB
|
||||
NSUInteger diskCapacity = 150 * 1024 * 1024; // 150MB
|
||||
NSURL *cacheURL = [[[NSFileManager defaultManager] URLForDirectory:NSCachesDirectory
|
||||
inDomain:NSUserDomainMask
|
||||
appropriateForURL:nil
|
||||
create:YES
|
||||
error:nil]
|
||||
URLByAppendingPathComponent:@"com.alamofire.imagedownloader"];
|
||||
|
||||
#if TARGET_OS_MACCATALYST
|
||||
return [[NSURLCache alloc] initWithMemoryCapacity:memoryCapacity
|
||||
diskCapacity:diskCapacity
|
||||
directoryURL:cacheURL];
|
||||
#else
|
||||
return [[NSURLCache alloc] initWithMemoryCapacity:memoryCapacity
|
||||
diskCapacity:diskCapacity
|
||||
diskPath:[cacheURL path]];
|
||||
#endif
|
||||
}
|
||||
|
||||
+ (NSURLSessionConfiguration *)defaultURLSessionConfiguration {
|
||||
@ -133,7 +147,11 @@
|
||||
|
||||
- (instancetype)init {
|
||||
NSURLSessionConfiguration *defaultConfiguration = [self.class defaultURLSessionConfiguration];
|
||||
AFHTTPSessionManager *sessionManager = [[AFHTTPSessionManager alloc] initWithSessionConfiguration:defaultConfiguration];
|
||||
return [self initWithSessionConfiguration:defaultConfiguration];
|
||||
}
|
||||
|
||||
- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration {
|
||||
AFHTTPSessionManager *sessionManager = [[AFHTTPSessionManager alloc] initWithSessionConfiguration:configuration];
|
||||
sessionManager.responseSerializer = [AFImageResponseSerializer serializer];
|
||||
|
||||
return [self initWithSessionManager:sessionManager
|
||||
@ -149,7 +167,7 @@
|
||||
if (self = [super init]) {
|
||||
self.sessionManager = sessionManager;
|
||||
|
||||
self.downloadPrioritizaton = downloadPrioritization;
|
||||
self.downloadPrioritization = downloadPrioritization;
|
||||
self.maximumActiveDownloads = maximumActiveDownloads;
|
||||
self.imageCache = imageCache;
|
||||
|
||||
@ -235,27 +253,31 @@
|
||||
|
||||
createdTask = [self.sessionManager
|
||||
dataTaskWithRequest:request
|
||||
uploadProgress:nil
|
||||
downloadProgress:nil
|
||||
completionHandler:^(NSURLResponse * _Nonnull response, id _Nullable responseObject, NSError * _Nullable error) {
|
||||
dispatch_async(self.responseQueue, ^{
|
||||
__strong __typeof__(weakSelf) strongSelf = weakSelf;
|
||||
AFImageDownloaderMergedTask *mergedTask = self.mergedTasks[URLIdentifier];
|
||||
AFImageDownloaderMergedTask *mergedTask = [strongSelf safelyGetMergedTask:URLIdentifier];
|
||||
if ([mergedTask.identifier isEqual:mergedTaskIdentifier]) {
|
||||
mergedTask = [strongSelf safelyRemoveMergedTaskWithURLIdentifier:URLIdentifier];
|
||||
if (error) {
|
||||
for (AFImageDownloaderResponseHandler *handler in mergedTask.responseHandlers) {
|
||||
if (handler.failureBlock) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
handler.failureBlock(request, (NSHTTPURLResponse*)response, error);
|
||||
handler.failureBlock(request, (NSHTTPURLResponse *)response, error);
|
||||
});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
[strongSelf.imageCache addImage:responseObject forRequest:request withAdditionalIdentifier:nil];
|
||||
if ([strongSelf.imageCache shouldCacheImage:responseObject forRequest:request withAdditionalIdentifier:nil]) {
|
||||
[strongSelf.imageCache addImage:responseObject forRequest:request withAdditionalIdentifier:nil];
|
||||
}
|
||||
|
||||
for (AFImageDownloaderResponseHandler *handler in mergedTask.responseHandlers) {
|
||||
if (handler.successBlock) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
handler.successBlock(request, (NSHTTPURLResponse*)response, responseObject);
|
||||
handler.successBlock(request, (NSHTTPURLResponse *)response, responseObject);
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -315,14 +337,14 @@
|
||||
}
|
||||
}
|
||||
|
||||
if (mergedTask.responseHandlers.count == 0 && mergedTask.task.state == NSURLSessionTaskStateSuspended) {
|
||||
if (mergedTask.responseHandlers.count == 0) {
|
||||
[mergedTask.task cancel];
|
||||
[self removeMergedTaskWithURLIdentifier:URLIdentifier];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
- (AFImageDownloaderMergedTask*)safelyRemoveMergedTaskWithURLIdentifier:(NSString *)URLIdentifier {
|
||||
- (AFImageDownloaderMergedTask *)safelyRemoveMergedTaskWithURLIdentifier:(NSString *)URLIdentifier {
|
||||
__block AFImageDownloaderMergedTask *mergedTask = nil;
|
||||
dispatch_sync(self.synchronizationQueue, ^{
|
||||
mergedTask = [self removeMergedTaskWithURLIdentifier:URLIdentifier];
|
||||
@ -365,7 +387,7 @@
|
||||
}
|
||||
|
||||
- (void)enqueueMergedTask:(AFImageDownloaderMergedTask *)mergedTask {
|
||||
switch (self.downloadPrioritizaton) {
|
||||
switch (self.downloadPrioritization) {
|
||||
case AFImageDownloadPrioritizationFIFO:
|
||||
[self.queuedMergedTasks addObject:mergedTask];
|
||||
break;
|
||||
@ -386,6 +408,14 @@
|
||||
return self.activeRequestCount < self.maximumActiveDownloads;
|
||||
}
|
||||
|
||||
- (AFImageDownloaderMergedTask *)safelyGetMergedTask:(NSString *)URLIdentifier {
|
||||
__block AFImageDownloaderMergedTask *mergedTask;
|
||||
dispatch_sync(self.synchronizationQueue, ^(){
|
||||
mergedTask = self.mergedTasks[URLIdentifier];
|
||||
});
|
||||
return mergedTask;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#endif
|
||||
|
||||
@ -109,11 +109,9 @@ typedef void (^AFNetworkActivityActionBlock)(BOOL networkActivityIndicatorVisibl
|
||||
|
||||
- (void)setNetworkActivityIndicatorVisible:(BOOL)networkActivityIndicatorVisible {
|
||||
if (_networkActivityIndicatorVisible != networkActivityIndicatorVisible) {
|
||||
[self willChangeValueForKey:@"networkActivityIndicatorVisible"];
|
||||
@synchronized(self) {
|
||||
_networkActivityIndicatorVisible = networkActivityIndicatorVisible;
|
||||
_networkActivityIndicatorVisible = networkActivityIndicatorVisible;
|
||||
}
|
||||
[self didChangeValueForKey:@"networkActivityIndicatorVisible"];
|
||||
if (self.networkActivityActionBlock) {
|
||||
self.networkActivityActionBlock(networkActivityIndicatorVisible);
|
||||
} else {
|
||||
@ -122,38 +120,20 @@ typedef void (^AFNetworkActivityActionBlock)(BOOL networkActivityIndicatorVisibl
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setActivityCount:(NSInteger)activityCount {
|
||||
@synchronized(self) {
|
||||
_activityCount = activityCount;
|
||||
}
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self updateCurrentStateForNetworkActivityChange];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)incrementActivityCount {
|
||||
[self willChangeValueForKey:@"activityCount"];
|
||||
@synchronized(self) {
|
||||
_activityCount++;
|
||||
}
|
||||
[self didChangeValueForKey:@"activityCount"];
|
||||
|
||||
@synchronized(self) {
|
||||
self.activityCount++;
|
||||
}
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self updateCurrentStateForNetworkActivityChange];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)decrementActivityCount {
|
||||
[self willChangeValueForKey:@"activityCount"];
|
||||
@synchronized(self) {
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wgnu"
|
||||
_activityCount = MAX(_activityCount - 1, 0);
|
||||
#pragma clang diagnostic pop
|
||||
}
|
||||
[self didChangeValueForKey:@"activityCount"];
|
||||
|
||||
@synchronized(self) {
|
||||
self.activityCount = MAX(_activityCount - 1, 0);
|
||||
}
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self updateCurrentStateForNetworkActivityChange];
|
||||
});
|
||||
@ -175,7 +155,6 @@ typedef void (^AFNetworkActivityActionBlock)(BOOL networkActivityIndicatorVisibl
|
||||
- (void)setCurrentState:(AFNetworkActivityManagerState)currentState {
|
||||
@synchronized(self) {
|
||||
if (_currentState != currentState) {
|
||||
[self willChangeValueForKey:@"currentState"];
|
||||
_currentState = currentState;
|
||||
switch (currentState) {
|
||||
case AFNetworkActivityManagerStateNotActive:
|
||||
@ -195,7 +174,6 @@ typedef void (^AFNetworkActivityActionBlock)(BOOL networkActivityIndicatorVisibl
|
||||
break;
|
||||
}
|
||||
}
|
||||
[self didChangeValueForKey:@"currentState"];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -71,16 +71,12 @@
|
||||
|
||||
if (task) {
|
||||
if (task.state != NSURLSessionTaskStateCompleted) {
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wreceiver-is-weak"
|
||||
#pragma clang diagnostic ignored "-Warc-repeated-use-of-weak"
|
||||
UIActivityIndicatorView *activityIndicatorView = self.activityIndicatorView;
|
||||
if (task.state == NSURLSessionTaskStateRunning) {
|
||||
[self.activityIndicatorView startAnimating];
|
||||
[activityIndicatorView startAnimating];
|
||||
} else {
|
||||
[self.activityIndicatorView stopAnimating];
|
||||
[activityIndicatorView stopAnimating];
|
||||
}
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
[notificationCenter addObserver:self selector:@selector(af_startAnimating) name:AFNetworkingTaskDidResumeNotification object:task];
|
||||
[notificationCenter addObserver:self selector:@selector(af_stopAnimating) name:AFNetworkingTaskDidCompleteNotification object:task];
|
||||
@ -93,19 +89,13 @@
|
||||
|
||||
- (void)af_startAnimating {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wreceiver-is-weak"
|
||||
[self.activityIndicatorView startAnimating];
|
||||
#pragma clang diagnostic pop
|
||||
});
|
||||
}
|
||||
|
||||
- (void)af_stopAnimating {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wreceiver-is-weak"
|
||||
[self.activityIndicatorView stopAnimating];
|
||||
#pragma clang diagnostic pop
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -103,14 +103,11 @@ static const char * af_backgroundImageDownloadReceiptKeyForState(UIControlState
|
||||
|
||||
+ (AFImageDownloader *)sharedImageDownloader {
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wgnu"
|
||||
return objc_getAssociatedObject(self, @selector(sharedImageDownloader)) ?: [AFImageDownloader defaultInstance];
|
||||
#pragma clang diagnostic pop
|
||||
return objc_getAssociatedObject([UIButton class], @selector(sharedImageDownloader)) ?: [AFImageDownloader defaultInstance];
|
||||
}
|
||||
|
||||
+ (void)setSharedImageDownloader:(AFImageDownloader *)imageDownloader {
|
||||
objc_setAssociatedObject(self, @selector(sharedImageDownloader), imageDownloader, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
|
||||
objc_setAssociatedObject([UIButton class], @selector(sharedImageDownloader), imageDownloader, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
@ -171,7 +168,7 @@ static const char * af_backgroundImageDownloadReceiptKeyForState(UIControlState
|
||||
if ([[strongSelf af_imageDownloadReceiptForState:state].receiptID isEqual:downloadID]) {
|
||||
if (success) {
|
||||
success(request, response, responseObject);
|
||||
} else if(responseObject) {
|
||||
} else if (responseObject) {
|
||||
[strongSelf setImage:responseObject forState:state];
|
||||
}
|
||||
[strongSelf af_setImageDownloadReceipt:nil forState:state];
|
||||
@ -250,7 +247,7 @@ static const char * af_backgroundImageDownloadReceiptKeyForState(UIControlState
|
||||
if ([[strongSelf af_backgroundImageDownloadReceiptForState:state].receiptID isEqual:downloadID]) {
|
||||
if (success) {
|
||||
success(request, response, responseObject);
|
||||
} else if(responseObject) {
|
||||
} else if (responseObject) {
|
||||
[strongSelf setBackgroundImage:responseObject forState:state];
|
||||
}
|
||||
[strongSelf af_setBackgroundImageDownloadReceipt:nil forState:state];
|
||||
|
||||
@ -48,15 +48,11 @@
|
||||
@implementation UIImageView (AFNetworking)
|
||||
|
||||
+ (AFImageDownloader *)sharedImageDownloader {
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wgnu"
|
||||
return objc_getAssociatedObject(self, @selector(sharedImageDownloader)) ?: [AFImageDownloader defaultInstance];
|
||||
#pragma clang diagnostic pop
|
||||
return objc_getAssociatedObject([UIImageView class], @selector(sharedImageDownloader)) ?: [AFImageDownloader defaultInstance];
|
||||
}
|
||||
|
||||
+ (void)setSharedImageDownloader:(AFImageDownloader *)imageDownloader {
|
||||
objc_setAssociatedObject(self, @selector(sharedImageDownloader), imageDownloader, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
|
||||
objc_setAssociatedObject([UIImageView class], @selector(sharedImageDownloader), imageDownloader, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
@ -79,17 +75,19 @@
|
||||
success:(void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, UIImage *image))success
|
||||
failure:(void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, NSError *error))failure
|
||||
{
|
||||
|
||||
if ([urlRequest URL] == nil) {
|
||||
[self cancelImageDownloadTask];
|
||||
self.image = placeholderImage;
|
||||
if (failure) {
|
||||
NSError *error = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorBadURL userInfo:nil];
|
||||
failure(urlRequest, nil, error);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if ([self isActiveTaskURLEqualToURLRequest:urlRequest]){
|
||||
|
||||
if ([self isActiveTaskURLEqualToURLRequest:urlRequest]) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
[self cancelImageDownloadTask];
|
||||
|
||||
AFImageDownloader *downloader = [[self class] sharedImageDownloader];
|
||||
@ -120,7 +118,7 @@
|
||||
if ([strongSelf.af_activeImageDownloadReceipt.receiptID isEqual:downloadID]) {
|
||||
if (success) {
|
||||
success(request, response, responseObject);
|
||||
} else if(responseObject) {
|
||||
} else if (responseObject) {
|
||||
strongSelf.image = responseObject;
|
||||
}
|
||||
[strongSelf clearActiveDownloadInformation];
|
||||
|
||||
@ -20,23 +20,24 @@
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
#if TARGET_OS_IOS || TARGET_OS_TV
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <TargetConditionals.h>
|
||||
|
||||
#ifndef _UIKIT_AFNETWORKING_
|
||||
#define _UIKIT_AFNETWORKING_
|
||||
|
||||
#if TARGET_OS_IOS
|
||||
#if TARGET_OS_IOS || TARGET_OS_TV
|
||||
#import "AFAutoPurgingImageCache.h"
|
||||
#import "AFImageDownloader.h"
|
||||
#import "AFNetworkActivityIndicatorManager.h"
|
||||
#import "UIRefreshControl+AFNetworking.h"
|
||||
#import "UIWebView+AFNetworking.h"
|
||||
#endif
|
||||
|
||||
#import "UIActivityIndicatorView+AFNetworking.h"
|
||||
#import "UIButton+AFNetworking.h"
|
||||
#import "UIImageView+AFNetworking.h"
|
||||
#import "UIProgressView+AFNetworking.h"
|
||||
#endif /* _UIKIT_AFNETWORKING_ */
|
||||
#endif
|
||||
|
||||
#if TARGET_OS_IOS
|
||||
#import "AFNetworkActivityIndicatorManager.h"
|
||||
#import "UIRefreshControl+AFNetworking.h"
|
||||
#import "WKWebView+AFNetworking.h"
|
||||
#endif
|
||||
|
||||
#endif /* _UIKIT_AFNETWORKING_ */
|
||||
|
||||
@ -55,6 +55,10 @@ static void * AFTaskCountOfBytesReceivedContext = &AFTaskCountOfBytesReceivedCon
|
||||
- (void)setProgressWithUploadProgressOfTask:(NSURLSessionUploadTask *)task
|
||||
animated:(BOOL)animated
|
||||
{
|
||||
if (task.state == NSURLSessionTaskStateCompleted) {
|
||||
return;
|
||||
}
|
||||
|
||||
[task addObserver:self forKeyPath:@"state" options:(NSKeyValueObservingOptions)0 context:AFTaskCountOfBytesSentContext];
|
||||
[task addObserver:self forKeyPath:@"countOfBytesSent" options:(NSKeyValueObservingOptions)0 context:AFTaskCountOfBytesSentContext];
|
||||
|
||||
@ -64,6 +68,10 @@ static void * AFTaskCountOfBytesReceivedContext = &AFTaskCountOfBytesReceivedCon
|
||||
- (void)setProgressWithDownloadProgressOfTask:(NSURLSessionDownloadTask *)task
|
||||
animated:(BOOL)animated
|
||||
{
|
||||
if (task.state == NSURLSessionTaskStateCompleted) {
|
||||
return;
|
||||
}
|
||||
|
||||
[task addObserver:self forKeyPath:@"state" options:(NSKeyValueObservingOptions)0 context:AFTaskCountOfBytesReceivedContext];
|
||||
[task addObserver:self forKeyPath:@"countOfBytesReceived" options:(NSKeyValueObservingOptions)0 context:AFTaskCountOfBytesReceivedContext];
|
||||
|
||||
|
||||
@ -71,19 +71,16 @@
|
||||
[notificationCenter removeObserver:self name:AFNetworkingTaskDidCompleteNotification object:nil];
|
||||
|
||||
if (task) {
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wreceiver-is-weak"
|
||||
#pragma clang diagnostic ignored "-Warc-repeated-use-of-weak"
|
||||
UIRefreshControl *refreshControl = self.refreshControl;
|
||||
if (task.state == NSURLSessionTaskStateRunning) {
|
||||
[self.refreshControl beginRefreshing];
|
||||
[refreshControl beginRefreshing];
|
||||
|
||||
[notificationCenter addObserver:self selector:@selector(af_beginRefreshing) name:AFNetworkingTaskDidResumeNotification object:task];
|
||||
[notificationCenter addObserver:self selector:@selector(af_endRefreshing) name:AFNetworkingTaskDidCompleteNotification object:task];
|
||||
[notificationCenter addObserver:self selector:@selector(af_endRefreshing) name:AFNetworkingTaskDidSuspendNotification object:task];
|
||||
} else {
|
||||
[self.refreshControl endRefreshing];
|
||||
[refreshControl endRefreshing];
|
||||
}
|
||||
#pragma clang diagnostic pop
|
||||
}
|
||||
}
|
||||
|
||||
@ -91,19 +88,13 @@
|
||||
|
||||
- (void)af_beginRefreshing {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wreceiver-is-weak"
|
||||
[self.refreshControl beginRefreshing];
|
||||
#pragma clang diagnostic pop
|
||||
});
|
||||
}
|
||||
|
||||
- (void)af_endRefreshing {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wreceiver-is-weak"
|
||||
[self.refreshControl endRefreshing];
|
||||
#pragma clang diagnostic pop
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// UIWebView+AFNetworking.h
|
||||
// WkWebView+AFNetworking.h
|
||||
// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ )
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
@ -26,47 +26,47 @@
|
||||
#if TARGET_OS_IOS
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <WebKit/WebKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class AFHTTPSessionManager;
|
||||
|
||||
/**
|
||||
This category adds methods to the UIKit framework's `UIWebView` class. The methods in this category provide increased control over the request cycle, including progress monitoring and success / failure handling.
|
||||
|
||||
@discussion When using these category methods, make sure to assign `delegate` for the web view, which implements `–webView:shouldStartLoadWithRequest:navigationType:` appropriately. This allows for tapped links to be loaded through AFNetworking, and can ensure that `canGoBack` & `canGoForward` update their values correctly.
|
||||
*/
|
||||
@interface UIWebView (AFNetworking)
|
||||
@interface WKWebView (AFNetworking)
|
||||
|
||||
/**
|
||||
The session manager used to download all requests.
|
||||
The session manager used to download all request
|
||||
*/
|
||||
@property (nonatomic, strong) AFHTTPSessionManager *sessionManager;
|
||||
|
||||
/**
|
||||
Asynchronously loads the specified request.
|
||||
|
||||
|
||||
@param request A URL request identifying the location of the content to load. This must not be `nil`.
|
||||
@param navigation The WKNavigation object that containts information for tracking the loading progress of a webpage. This must not be `nil`.
|
||||
@param progress A progress object monitoring the current download progress.
|
||||
@param success A block object to be executed when the request finishes loading successfully. This block returns the HTML string to be loaded by the web view, and takes two arguments: the response, and the response string.
|
||||
@param failure A block object to be executed when the data task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a single argument: the error that occurred.
|
||||
*/
|
||||
- (void)loadRequest:(NSURLRequest *)request
|
||||
navigation:(WKNavigation * _Nonnull)navigation
|
||||
progress:(NSProgress * _Nullable __autoreleasing * _Nullable)progress
|
||||
success:(nullable NSString * (^)(NSHTTPURLResponse *response, NSString *HTML))success
|
||||
failure:(nullable void (^)(NSError *error))failure;
|
||||
|
||||
/**
|
||||
Asynchronously loads the data associated with a particular request with a specified MIME type and text encoding.
|
||||
|
||||
|
||||
@param request A URL request identifying the location of the content to load. This must not be `nil`.
|
||||
@param navigation The WKNavigation object that containts information for tracking the loading progress of a webpage. This must not be `nil`.
|
||||
@param MIMEType The MIME type of the content. Defaults to the content type of the response if not specified.
|
||||
@param textEncodingName The IANA encoding name, as in `utf-8` or `utf-16`. Defaults to the response text encoding if not specified.
|
||||
@param progress A progress object monitoring the current download progress.
|
||||
@param progress A progress object monitoring the current download progress.
|
||||
@param success A block object to be executed when the request finishes loading successfully. This block returns the data to be loaded by the web view and takes two arguments: the response, and the downloaded data.
|
||||
@param failure A block object to be executed when the data task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a single argument: the error that occurred.
|
||||
*/
|
||||
- (void)loadRequest:(NSURLRequest *)request
|
||||
navigation:(WKNavigation * _Nonnull)navigation
|
||||
MIMEType:(nullable NSString *)MIMEType
|
||||
textEncodingName:(nullable NSString *)textEncodingName
|
||||
progress:(NSProgress * _Nullable __autoreleasing * _Nullable)progress
|
||||
@ -1,4 +1,4 @@
|
||||
// UIWebView+AFNetworking.m
|
||||
// WkWebView+AFNetworking.m
|
||||
// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ )
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
@ -19,7 +19,7 @@
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
#import "UIWebView+AFNetworking.h"
|
||||
#import "WKWebView+AFNetworking.h"
|
||||
|
||||
#import <objc/runtime.h>
|
||||
|
||||
@ -29,11 +29,11 @@
|
||||
#import "AFURLResponseSerialization.h"
|
||||
#import "AFURLRequestSerialization.h"
|
||||
|
||||
@interface UIWebView (_AFNetworking)
|
||||
@interface WKWebView (_AFNetworking)
|
||||
@property (readwrite, nonatomic, strong, setter = af_setURLSessionTask:) NSURLSessionDataTask *af_URLSessionTask;
|
||||
@end
|
||||
|
||||
@implementation UIWebView (_AFNetworking)
|
||||
@implementation WKWebView (_AFNetworking)
|
||||
|
||||
- (NSURLSessionDataTask *)af_URLSessionTask {
|
||||
return (NSURLSessionDataTask *)objc_getAssociatedObject(self, @selector(af_URLSessionTask));
|
||||
@ -47,9 +47,9 @@
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@implementation UIWebView (AFNetworking)
|
||||
@implementation WKWebView (AFNetworking)
|
||||
|
||||
- (AFHTTPSessionManager *)sessionManager {
|
||||
- (AFHTTPSessionManager *)sessionManager {
|
||||
static AFHTTPSessionManager *_af_defaultHTTPSessionManager = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
@ -57,11 +57,8 @@
|
||||
_af_defaultHTTPSessionManager.requestSerializer = [AFHTTPRequestSerializer serializer];
|
||||
_af_defaultHTTPSessionManager.responseSerializer = [AFHTTPResponseSerializer serializer];
|
||||
});
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wgnu"
|
||||
|
||||
return objc_getAssociatedObject(self, @selector(sessionManager)) ?: _af_defaultHTTPSessionManager;
|
||||
#pragma clang diagnostic pop
|
||||
}
|
||||
|
||||
- (void)setSessionManager:(AFHTTPSessionManager *)sessionManager {
|
||||
@ -74,11 +71,8 @@
|
||||
dispatch_once(&onceToken, ^{
|
||||
_af_defaultResponseSerializer = [AFHTTPResponseSerializer serializer];
|
||||
});
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wgnu"
|
||||
|
||||
return objc_getAssociatedObject(self, @selector(responseSerializer)) ?: _af_defaultResponseSerializer;
|
||||
#pragma clang diagnostic pop
|
||||
}
|
||||
|
||||
- (void)setResponseSerializer:(AFHTTPResponseSerializer<AFURLResponseSerialization> *)responseSerializer {
|
||||
@ -88,11 +82,11 @@
|
||||
#pragma mark -
|
||||
|
||||
- (void)loadRequest:(NSURLRequest *)request
|
||||
navigation:(WKNavigation * _Nonnull)navigation
|
||||
progress:(NSProgress * _Nullable __autoreleasing * _Nullable)progress
|
||||
success:(NSString * (^)(NSHTTPURLResponse *response, NSString *HTML))success
|
||||
failure:(void (^)(NSError *error))failure
|
||||
{
|
||||
[self loadRequest:request MIMEType:nil textEncodingName:nil progress:progress success:^NSData *(NSHTTPURLResponse *response, NSData *data) {
|
||||
success:(nullable NSString * (^)(NSHTTPURLResponse *response, NSString *HTML))success
|
||||
failure:(nullable void (^)(NSError *error))failure {
|
||||
[self loadRequest:request navigation:navigation MIMEType:nil textEncodingName:nil progress:progress success:^NSData * _Nonnull(NSHTTPURLResponse * _Nonnull response, NSData * _Nonnull data) {
|
||||
NSStringEncoding stringEncoding = NSUTF8StringEncoding;
|
||||
if (response.textEncodingName) {
|
||||
CFStringEncoding encoding = CFStringConvertIANACharSetNameToEncoding((CFStringRef)response.textEncodingName);
|
||||
@ -100,63 +94,61 @@
|
||||
stringEncoding = CFStringConvertEncodingToNSStringEncoding(encoding);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
NSString *string = [[NSString alloc] initWithData:data encoding:stringEncoding];
|
||||
if (success) {
|
||||
string = success(response, string);
|
||||
}
|
||||
|
||||
|
||||
return [string dataUsingEncoding:stringEncoding];
|
||||
} failure:failure];
|
||||
}
|
||||
|
||||
- (void)loadRequest:(NSURLRequest *)request
|
||||
MIMEType:(NSString *)MIMEType
|
||||
textEncodingName:(NSString *)textEncodingName
|
||||
navigation:(WKNavigation * _Nonnull)navigation
|
||||
MIMEType:(nullable NSString *)MIMEType
|
||||
textEncodingName:(nullable NSString *)textEncodingName
|
||||
progress:(NSProgress * _Nullable __autoreleasing * _Nullable)progress
|
||||
success:(NSData * (^)(NSHTTPURLResponse *response, NSData *data))success
|
||||
failure:(void (^)(NSError *error))failure
|
||||
{
|
||||
success:(nullable NSData * (^)(NSHTTPURLResponse *response, NSData *data))success
|
||||
failure:(nullable void (^)(NSError *error))failure {
|
||||
NSParameterAssert(request);
|
||||
|
||||
|
||||
if (self.af_URLSessionTask.state == NSURLSessionTaskStateRunning || self.af_URLSessionTask.state == NSURLSessionTaskStateSuspended) {
|
||||
[self.af_URLSessionTask cancel];
|
||||
}
|
||||
self.af_URLSessionTask = nil;
|
||||
|
||||
|
||||
__weak __typeof(self)weakSelf = self;
|
||||
NSURLSessionDataTask *dataTask;
|
||||
dataTask = [self.sessionManager
|
||||
GET:request.URL.absoluteString
|
||||
parameters:nil
|
||||
progress:nil
|
||||
success:^(NSURLSessionDataTask * _Nonnull task, id _Nonnull responseObject) {
|
||||
__strong __typeof(weakSelf) strongSelf = weakSelf;
|
||||
if (success) {
|
||||
success((NSHTTPURLResponse *)task.response, responseObject);
|
||||
}
|
||||
[strongSelf loadData:responseObject MIMEType:MIMEType textEncodingName:textEncodingName baseURL:[task.currentRequest URL]];
|
||||
|
||||
if ([strongSelf.delegate respondsToSelector:@selector(webViewDidStartLoad:)]) {
|
||||
[strongSelf.delegate webViewDidFinishLoad:strongSelf];
|
||||
}
|
||||
__block NSURLSessionDataTask *dataTask;
|
||||
__strong __typeof(weakSelf) strongSelf = weakSelf;
|
||||
__strong __typeof(weakSelf.navigationDelegate) strongSelfDelegate = strongSelf.navigationDelegate;
|
||||
dataTask = [self.sessionManager dataTaskWithRequest:request uploadProgress:nil downloadProgress:nil completionHandler:^(NSURLResponse * _Nonnull response, id _Nullable responseObject, NSError * _Nullable error) {
|
||||
if (error) {
|
||||
if (failure) {
|
||||
failure(error);
|
||||
}
|
||||
failure:^(NSURLSessionDataTask * _Nonnull task, NSError * _Nonnull error) {
|
||||
if (failure) {
|
||||
failure(error);
|
||||
}
|
||||
}];
|
||||
} else {
|
||||
if (success) {
|
||||
success((NSHTTPURLResponse *)response, responseObject);
|
||||
}
|
||||
[strongSelf loadData:responseObject MIMEType:MIMEType characterEncodingName:textEncodingName baseURL:[dataTask.currentRequest URL]];
|
||||
|
||||
if ([strongSelfDelegate respondsToSelector:@selector(webView:didFinishNavigation:)]) {
|
||||
[strongSelfDelegate webView:strongSelf didFinishNavigation:navigation];
|
||||
}
|
||||
}
|
||||
}];
|
||||
self.af_URLSessionTask = dataTask;
|
||||
if (progress != nil) {
|
||||
*progress = [self.sessionManager downloadProgressForTask:dataTask];
|
||||
}
|
||||
[self.af_URLSessionTask resume];
|
||||
|
||||
if ([self.delegate respondsToSelector:@selector(webViewDidStartLoad:)]) {
|
||||
[self.delegate webViewDidStartLoad:self];
|
||||
|
||||
if ([strongSelfDelegate respondsToSelector:@selector(webView:didStartProvisionalNavigation:)]) {
|
||||
[strongSelfDelegate webView:self didStartProvisionalNavigation:navigation];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#endif
|
||||
#endif
|
||||
@ -1,42 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 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 <Foundation/Foundation.h>
|
||||
|
||||
#import <Bolts/BFCancellationTokenRegistration.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/*!
|
||||
A block that will be called when a token is cancelled.
|
||||
*/
|
||||
typedef void(^BFCancellationBlock)(void);
|
||||
|
||||
/*!
|
||||
The consumer view of a CancellationToken.
|
||||
Propagates notification that operations should be canceled.
|
||||
A BFCancellationToken has methods to inspect whether the token has been cancelled.
|
||||
*/
|
||||
@interface BFCancellationToken : NSObject
|
||||
|
||||
/*!
|
||||
Whether cancellation has been requested for this token source.
|
||||
*/
|
||||
@property (nonatomic, assign, readonly, getter=isCancellationRequested) BOOL cancellationRequested;
|
||||
|
||||
/*!
|
||||
Register a block to be notified when the token is cancelled.
|
||||
If the token is already cancelled the delegate will be notified immediately.
|
||||
*/
|
||||
- (BFCancellationTokenRegistration *)registerCancellationObserverWithBlock:(BFCancellationBlock)block;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
144
Example/Pods/Bolts/Bolts/Common/BFCancellationToken.m
generated
144
Example/Pods/Bolts/Bolts/Common/BFCancellationToken.m
generated
@ -1,144 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 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 "BFCancellationToken.h"
|
||||
#import "BFCancellationTokenRegistration.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface BFCancellationToken ()
|
||||
|
||||
@property (nullable, nonatomic, strong) NSMutableArray *registrations;
|
||||
@property (nonatomic, strong) NSObject *lock;
|
||||
@property (nonatomic) BOOL disposed;
|
||||
|
||||
@end
|
||||
|
||||
@interface BFCancellationTokenRegistration (BFCancellationToken)
|
||||
|
||||
+ (instancetype)registrationWithToken:(BFCancellationToken *)token delegate:(BFCancellationBlock)delegate;
|
||||
|
||||
- (void)notifyDelegate;
|
||||
|
||||
@end
|
||||
|
||||
@implementation BFCancellationToken
|
||||
|
||||
@synthesize cancellationRequested = _cancellationRequested;
|
||||
|
||||
#pragma mark - Initializer
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
if (!self) return self;
|
||||
|
||||
_registrations = [NSMutableArray array];
|
||||
_lock = [NSObject new];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark - Custom Setters/Getters
|
||||
|
||||
- (BOOL)isCancellationRequested {
|
||||
@synchronized(self.lock) {
|
||||
[self throwIfDisposed];
|
||||
return _cancellationRequested;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)cancel {
|
||||
NSArray *registrations;
|
||||
@synchronized(self.lock) {
|
||||
[self throwIfDisposed];
|
||||
if (_cancellationRequested) {
|
||||
return;
|
||||
}
|
||||
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(cancelPrivate) object:nil];
|
||||
_cancellationRequested = YES;
|
||||
registrations = [self.registrations copy];
|
||||
}
|
||||
|
||||
[self notifyCancellation:registrations];
|
||||
}
|
||||
|
||||
- (void)notifyCancellation:(NSArray *)registrations {
|
||||
for (BFCancellationTokenRegistration *registration in registrations) {
|
||||
[registration notifyDelegate];
|
||||
}
|
||||
}
|
||||
|
||||
- (BFCancellationTokenRegistration *)registerCancellationObserverWithBlock:(BFCancellationBlock)block {
|
||||
@synchronized(self.lock) {
|
||||
BFCancellationTokenRegistration *registration = [BFCancellationTokenRegistration registrationWithToken:self delegate:[block copy]];
|
||||
[self.registrations addObject:registration];
|
||||
|
||||
return registration;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)unregisterRegistration:(BFCancellationTokenRegistration *)registration {
|
||||
@synchronized(self.lock) {
|
||||
[self throwIfDisposed];
|
||||
[self.registrations removeObject:registration];
|
||||
}
|
||||
}
|
||||
|
||||
// Delay on a non-public method to prevent interference with a user calling performSelector or
|
||||
// cancelPreviousPerformRequestsWithTarget on the public method
|
||||
- (void)cancelPrivate {
|
||||
[self cancel];
|
||||
}
|
||||
|
||||
- (void)cancelAfterDelay:(int)millis {
|
||||
[self throwIfDisposed];
|
||||
if (millis < -1) {
|
||||
[NSException raise:NSInvalidArgumentException format:@"Delay must be >= -1"];
|
||||
}
|
||||
|
||||
if (millis == 0) {
|
||||
[self cancel];
|
||||
return;
|
||||
}
|
||||
|
||||
@synchronized(self.lock) {
|
||||
[self throwIfDisposed];
|
||||
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(cancelPrivate) object:nil];
|
||||
if (self.cancellationRequested) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (millis != -1) {
|
||||
double delay = (double)millis / 1000;
|
||||
[self performSelector:@selector(cancelPrivate) withObject:nil afterDelay:delay];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)dispose {
|
||||
@synchronized(self.lock) {
|
||||
if (self.disposed) {
|
||||
return;
|
||||
}
|
||||
[self.registrations makeObjectsPerformSelector:@selector(dispose)];
|
||||
self.registrations = nil;
|
||||
self.disposed = YES;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)throwIfDisposed {
|
||||
if (self.disposed) {
|
||||
[NSException raise:NSInternalInconsistencyException format:@"Object already disposed"];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@ -1,29 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 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 <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/*!
|
||||
Represents the registration of a cancellation observer with a cancellation token.
|
||||
Can be used to unregister the observer at a later time.
|
||||
*/
|
||||
@interface BFCancellationTokenRegistration : NSObject
|
||||
|
||||
/*!
|
||||
Removes the cancellation observer registered with the token
|
||||
and releases all resources associated with this registration.
|
||||
*/
|
||||
- (void)dispose;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@ -1,79 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 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 "BFCancellationTokenRegistration.h"
|
||||
|
||||
#import "BFCancellationToken.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface BFCancellationTokenRegistration ()
|
||||
|
||||
@property (nonatomic, weak) BFCancellationToken *token;
|
||||
@property (nullable, nonatomic, strong) BFCancellationBlock cancellationObserverBlock;
|
||||
@property (nonatomic, strong) NSObject *lock;
|
||||
@property (nonatomic) BOOL disposed;
|
||||
|
||||
@end
|
||||
|
||||
@interface BFCancellationToken (BFCancellationTokenRegistration)
|
||||
|
||||
- (void)unregisterRegistration:(BFCancellationTokenRegistration *)registration;
|
||||
|
||||
@end
|
||||
|
||||
@implementation BFCancellationTokenRegistration
|
||||
|
||||
+ (instancetype)registrationWithToken:(BFCancellationToken *)token delegate:(BFCancellationBlock)delegate {
|
||||
BFCancellationTokenRegistration *registration = [BFCancellationTokenRegistration new];
|
||||
registration.token = token;
|
||||
registration.cancellationObserverBlock = delegate;
|
||||
return registration;
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
if (!self) return self;
|
||||
|
||||
_lock = [NSObject new];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dispose {
|
||||
@synchronized(self.lock) {
|
||||
if (self.disposed) {
|
||||
return;
|
||||
}
|
||||
self.disposed = YES;
|
||||
}
|
||||
|
||||
BFCancellationToken *token = self.token;
|
||||
if (token != nil) {
|
||||
[token unregisterRegistration:self];
|
||||
self.token = nil;
|
||||
}
|
||||
self.cancellationObserverBlock = nil;
|
||||
}
|
||||
|
||||
- (void)notifyDelegate {
|
||||
@synchronized(self.lock) {
|
||||
[self throwIfDisposed];
|
||||
self.cancellationObserverBlock();
|
||||
}
|
||||
}
|
||||
|
||||
- (void)throwIfDisposed {
|
||||
NSAssert(!self.disposed, @"Object already disposed");
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@ -1,60 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 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 <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class BFCancellationToken;
|
||||
|
||||
/*!
|
||||
BFCancellationTokenSource represents the producer side of a CancellationToken.
|
||||
Signals to a CancellationToken that it should be canceled.
|
||||
It is a cancellation token that also has methods
|
||||
for changing the state of a token by cancelling it.
|
||||
*/
|
||||
@interface BFCancellationTokenSource : NSObject
|
||||
|
||||
/*!
|
||||
Creates a new cancellation token source.
|
||||
*/
|
||||
+ (instancetype)cancellationTokenSource;
|
||||
|
||||
/*!
|
||||
The cancellation token associated with this CancellationTokenSource.
|
||||
*/
|
||||
@property (nonatomic, strong, readonly) BFCancellationToken *token;
|
||||
|
||||
/*!
|
||||
Whether cancellation has been requested for this token source.
|
||||
*/
|
||||
@property (nonatomic, assign, readonly, getter=isCancellationRequested) BOOL cancellationRequested;
|
||||
|
||||
/*!
|
||||
Cancels the token if it has not already been cancelled.
|
||||
*/
|
||||
- (void)cancel;
|
||||
|
||||
/*!
|
||||
Schedules a cancel operation on this CancellationTokenSource after the specified number of milliseconds.
|
||||
@param millis The number of milliseconds to wait before completing the returned task.
|
||||
If delay is `0` the cancel is executed immediately. If delay is `-1` any scheduled cancellation is stopped.
|
||||
*/
|
||||
- (void)cancelAfterDelay:(int)millis;
|
||||
|
||||
/*!
|
||||
Releases all resources associated with this token source,
|
||||
including disposing of all registrations.
|
||||
*/
|
||||
- (void)dispose;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@ -1,64 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 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 "BFCancellationTokenSource.h"
|
||||
|
||||
#import "BFCancellationToken.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface BFCancellationToken (BFCancellationTokenSource)
|
||||
|
||||
- (void)cancel;
|
||||
- (void)cancelAfterDelay:(int)millis;
|
||||
|
||||
- (void)dispose;
|
||||
- (void)throwIfDisposed;
|
||||
|
||||
@end
|
||||
|
||||
@implementation BFCancellationTokenSource
|
||||
|
||||
#pragma mark - Initializer
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
if (!self) return self;
|
||||
|
||||
_token = [BFCancellationToken new];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
+ (instancetype)cancellationTokenSource {
|
||||
return [BFCancellationTokenSource new];
|
||||
}
|
||||
|
||||
#pragma mark - Custom Setters/Getters
|
||||
|
||||
- (BOOL)isCancellationRequested {
|
||||
return _token.isCancellationRequested;
|
||||
}
|
||||
|
||||
- (void)cancel {
|
||||
[_token cancel];
|
||||
}
|
||||
|
||||
- (void)cancelAfterDelay:(int)millis {
|
||||
[_token cancelAfterDelay:millis];
|
||||
}
|
||||
|
||||
- (void)dispose {
|
||||
[_token dispose];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
62
Example/Pods/Bolts/Bolts/Common/BFExecutor.h
generated
62
Example/Pods/Bolts/Bolts/Common/BFExecutor.h
generated
@ -1,62 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 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 <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/*!
|
||||
An object that can run a given block.
|
||||
*/
|
||||
@interface BFExecutor : NSObject
|
||||
|
||||
/*!
|
||||
Returns a default executor, which runs continuations immediately until the call stack gets too
|
||||
deep, then dispatches to a new GCD queue.
|
||||
*/
|
||||
+ (instancetype)defaultExecutor;
|
||||
|
||||
/*!
|
||||
Returns an executor that runs continuations on the thread where the previous task was completed.
|
||||
*/
|
||||
+ (instancetype)immediateExecutor;
|
||||
|
||||
/*!
|
||||
Returns an executor that runs continuations on the main thread.
|
||||
*/
|
||||
+ (instancetype)mainThreadExecutor;
|
||||
|
||||
/*!
|
||||
Returns a new executor that uses the given block to execute continuations.
|
||||
@param block The block to use.
|
||||
*/
|
||||
+ (instancetype)executorWithBlock:(void(^)(void(^block)(void)))block;
|
||||
|
||||
/*!
|
||||
Returns a new executor that runs continuations on the given queue.
|
||||
@param queue The instance of `dispatch_queue_t` to dispatch all continuations onto.
|
||||
*/
|
||||
+ (instancetype)executorWithDispatchQueue:(dispatch_queue_t)queue;
|
||||
|
||||
/*!
|
||||
Returns a new executor that runs continuations on the given queue.
|
||||
@param queue The instance of `NSOperationQueue` to run all continuations on.
|
||||
*/
|
||||
+ (instancetype)executorWithOperationQueue:(NSOperationQueue *)queue;
|
||||
|
||||
/*!
|
||||
Runs the given block using this executor's particular strategy.
|
||||
@param block The block to execute.
|
||||
*/
|
||||
- (void)execute:(void(^)(void))block;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
136
Example/Pods/Bolts/Bolts/Common/BFExecutor.m
generated
136
Example/Pods/Bolts/Bolts/Common/BFExecutor.m
generated
@ -1,136 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 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 "BFExecutor.h"
|
||||
|
||||
#import <pthread.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/*!
|
||||
Get the remaining stack-size of the current thread.
|
||||
|
||||
@param totalSize The total stack size of the current thread.
|
||||
|
||||
@return The remaining size, in bytes, available to the current thread.
|
||||
|
||||
@note This function cannot be inlined, as otherwise the internal implementation could fail to report the proper
|
||||
remaining stack space.
|
||||
*/
|
||||
__attribute__((noinline)) static size_t remaining_stack_size(size_t *restrict totalSize) {
|
||||
pthread_t currentThread = pthread_self();
|
||||
|
||||
// NOTE: We must store stack pointers as uint8_t so that the pointer math is well-defined
|
||||
uint8_t *endStack = pthread_get_stackaddr_np(currentThread);
|
||||
*totalSize = pthread_get_stacksize_np(currentThread);
|
||||
|
||||
// NOTE: If the function is inlined, this value could be incorrect
|
||||
uint8_t *frameAddr = __builtin_frame_address(0);
|
||||
|
||||
return (*totalSize) - (size_t)(endStack - frameAddr);
|
||||
}
|
||||
|
||||
@interface BFExecutor ()
|
||||
|
||||
@property (nonatomic, copy) void(^block)(void(^block)(void));
|
||||
|
||||
@end
|
||||
|
||||
@implementation BFExecutor
|
||||
|
||||
#pragma mark - Executor methods
|
||||
|
||||
+ (instancetype)defaultExecutor {
|
||||
static BFExecutor *defaultExecutor = NULL;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
defaultExecutor = [self executorWithBlock:^void(void(^block)(void)) {
|
||||
// We prefer to run everything possible immediately, so that there is callstack information
|
||||
// when debugging. However, we don't want the stack to get too deep, so if the remaining stack space
|
||||
// is less than 10% of the total space, we dispatch to another GCD queue.
|
||||
size_t totalStackSize = 0;
|
||||
size_t remainingStackSize = remaining_stack_size(&totalStackSize);
|
||||
|
||||
if (remainingStackSize < (totalStackSize / 10)) {
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), block);
|
||||
} else {
|
||||
@autoreleasepool {
|
||||
block();
|
||||
}
|
||||
}
|
||||
}];
|
||||
});
|
||||
return defaultExecutor;
|
||||
}
|
||||
|
||||
+ (instancetype)immediateExecutor {
|
||||
static BFExecutor *immediateExecutor = NULL;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
immediateExecutor = [self executorWithBlock:^void(void(^block)(void)) {
|
||||
block();
|
||||
}];
|
||||
});
|
||||
return immediateExecutor;
|
||||
}
|
||||
|
||||
+ (instancetype)mainThreadExecutor {
|
||||
static BFExecutor *mainThreadExecutor = NULL;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
mainThreadExecutor = [self executorWithBlock:^void(void(^block)(void)) {
|
||||
if (![NSThread isMainThread]) {
|
||||
dispatch_async(dispatch_get_main_queue(), block);
|
||||
} else {
|
||||
@autoreleasepool {
|
||||
block();
|
||||
}
|
||||
}
|
||||
}];
|
||||
});
|
||||
return mainThreadExecutor;
|
||||
}
|
||||
|
||||
+ (instancetype)executorWithBlock:(void(^)(void(^block)(void)))block {
|
||||
return [[self alloc] initWithBlock:block];
|
||||
}
|
||||
|
||||
+ (instancetype)executorWithDispatchQueue:(dispatch_queue_t)queue {
|
||||
return [self executorWithBlock:^void(void(^block)(void)) {
|
||||
dispatch_async(queue, block);
|
||||
}];
|
||||
}
|
||||
|
||||
+ (instancetype)executorWithOperationQueue:(NSOperationQueue *)queue {
|
||||
return [self executorWithBlock:^void(void(^block)(void)) {
|
||||
[queue addOperation:[NSBlockOperation blockOperationWithBlock:block]];
|
||||
}];
|
||||
}
|
||||
|
||||
#pragma mark - Initializer
|
||||
|
||||
- (instancetype)initWithBlock:(void(^)(void(^block)(void)))block {
|
||||
self = [super init];
|
||||
if (!self) return self;
|
||||
|
||||
_block = block;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark - Execution
|
||||
|
||||
- (void)execute:(void(^)(void))block {
|
||||
self.block(block);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
25
Example/Pods/Bolts/Bolts/Common/BFGeneric.h
generated
25
Example/Pods/Bolts/Bolts/Common/BFGeneric.h
generated
@ -1,25 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 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 <Foundation/Foundation.h>
|
||||
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
This exists to use along with `BFTask` and `BFTaskCompletionSource`.
|
||||
|
||||
Instead of returning a `BFTask` with no generic type, or a generic type of 'NSNull'
|
||||
when there is no usable result from a task, we use the type 'BFVoid', which will always have a value of `nil`.
|
||||
|
||||
This allows you to provide a more enforced API contract to the caller,
|
||||
as sending any message to `BFVoid` will result in a compile time error.
|
||||
*/
|
||||
@class _BFVoid_Nonexistant;
|
||||
typedef _BFVoid_Nonexistant *BFVoid;
|
||||
266
Example/Pods/Bolts/Bolts/Common/BFTask.h
generated
266
Example/Pods/Bolts/Bolts/Common/BFTask.h
generated
@ -1,266 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 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 <Foundation/Foundation.h>
|
||||
|
||||
#import <Bolts/BFCancellationToken.h>
|
||||
#import <Bolts/BFGeneric.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/*!
|
||||
Error domain used if there was multiple errors on <BFTask taskForCompletionOfAllTasks:>.
|
||||
*/
|
||||
extern NSString *const BFTaskErrorDomain;
|
||||
|
||||
/*!
|
||||
An error code used for <BFTask taskForCompletionOfAllTasks:>, if there were multiple errors.
|
||||
*/
|
||||
extern NSInteger const kBFMultipleErrorsError;
|
||||
|
||||
/*!
|
||||
An error userInfo key used if there were multiple errors on <BFTask taskForCompletionOfAllTasks:>.
|
||||
Value type is `NSArray<NSError *> *`.
|
||||
*/
|
||||
extern NSString *const BFTaskMultipleErrorsUserInfoKey;
|
||||
|
||||
@class BFExecutor;
|
||||
@class BFTask;
|
||||
|
||||
/*!
|
||||
The consumer view of a Task. A BFTask has methods to
|
||||
inspect the state of the task, and to add continuations to
|
||||
be run once the task is complete.
|
||||
*/
|
||||
@interface BFTask<__covariant ResultType> : NSObject
|
||||
|
||||
/*!
|
||||
A block that can act as a continuation for a task.
|
||||
*/
|
||||
typedef __nullable id(^BFContinuationBlock)(BFTask<ResultType> *t);
|
||||
|
||||
/*!
|
||||
Creates a task that is already completed with the given result.
|
||||
@param result The result for the task.
|
||||
*/
|
||||
+ (instancetype)taskWithResult:(nullable ResultType)result;
|
||||
|
||||
/*!
|
||||
Creates a task that is already completed with the given error.
|
||||
@param error The error for the task.
|
||||
*/
|
||||
+ (instancetype)taskWithError:(NSError *)error;
|
||||
|
||||
/*!
|
||||
Creates a task that is already cancelled.
|
||||
*/
|
||||
+ (instancetype)cancelledTask;
|
||||
|
||||
/*!
|
||||
Returns a task that will be completed (with result == nil) once
|
||||
all of the input tasks have completed.
|
||||
@param tasks An `NSArray` of the tasks to use as an input.
|
||||
*/
|
||||
+ (instancetype)taskForCompletionOfAllTasks:(nullable NSArray<BFTask *> *)tasks;
|
||||
|
||||
/*!
|
||||
Returns a task that will be completed once all of the input tasks have completed.
|
||||
If all tasks complete successfully without being faulted or cancelled the result will be
|
||||
an `NSArray` of all task results in the order they were provided.
|
||||
@param tasks An `NSArray` of the tasks to use as an input.
|
||||
*/
|
||||
+ (instancetype)taskForCompletionOfAllTasksWithResults:(nullable NSArray<BFTask *> *)tasks;
|
||||
|
||||
/*!
|
||||
Returns a task that will be completed once there is at least one successful task.
|
||||
The first task to successuly complete will set the result, all other tasks results are
|
||||
ignored.
|
||||
@param tasks An `NSArray` of the tasks to use as an input.
|
||||
*/
|
||||
+ (instancetype)taskForCompletionOfAnyTask:(nullable NSArray<BFTask *> *)tasks;
|
||||
|
||||
/*!
|
||||
Returns a task that will be completed a certain amount of time in the future.
|
||||
@param millis The approximate number of milliseconds to wait before the
|
||||
task will be finished (with result == nil).
|
||||
*/
|
||||
+ (BFTask<BFVoid> *)taskWithDelay:(int)millis;
|
||||
|
||||
/*!
|
||||
Returns a task that will be completed a certain amount of time in the future.
|
||||
@param millis The approximate number of milliseconds to wait before the
|
||||
task will be finished (with result == nil).
|
||||
@param token The cancellation token (optional).
|
||||
*/
|
||||
+ (BFTask<BFVoid> *)taskWithDelay:(int)millis cancellationToken:(nullable BFCancellationToken *)token;
|
||||
|
||||
/*!
|
||||
Returns a task that will be completed after the given block completes with
|
||||
the specified executor.
|
||||
@param executor A BFExecutor responsible for determining how the
|
||||
continuation block will be run.
|
||||
@param block The block to immediately schedule to run with the given executor.
|
||||
@returns A task that will be completed after block has run.
|
||||
If block returns a BFTask, then the task returned from
|
||||
this method will not be completed until that task is completed.
|
||||
*/
|
||||
+ (instancetype)taskFromExecutor:(BFExecutor *)executor withBlock:(nullable id (^)(void))block;
|
||||
|
||||
// Properties that will be set on the task once it is completed.
|
||||
|
||||
/*!
|
||||
The result of a successful task.
|
||||
*/
|
||||
@property (nullable, nonatomic, strong, readonly) ResultType result;
|
||||
|
||||
/*!
|
||||
The error of a failed task.
|
||||
*/
|
||||
@property (nullable, nonatomic, strong, readonly) NSError *error;
|
||||
|
||||
/*!
|
||||
Whether this task has been cancelled.
|
||||
*/
|
||||
@property (nonatomic, assign, readonly, getter=isCancelled) BOOL cancelled;
|
||||
|
||||
/*!
|
||||
Whether this task has completed due to an error.
|
||||
*/
|
||||
@property (nonatomic, assign, readonly, getter=isFaulted) BOOL faulted;
|
||||
|
||||
/*!
|
||||
Whether this task has completed.
|
||||
*/
|
||||
@property (nonatomic, assign, readonly, getter=isCompleted) BOOL completed;
|
||||
|
||||
/*!
|
||||
Enqueues the given block to be run once this task is complete.
|
||||
This method uses a default execution strategy. The block will be
|
||||
run on the thread where the previous task completes, unless the
|
||||
the stack depth is too deep, in which case it will be run on a
|
||||
dispatch queue with default priority.
|
||||
@param block The block to be run once this task is complete.
|
||||
@returns A task that will be completed after block has run.
|
||||
If block returns a BFTask, then the task returned from
|
||||
this method will not be completed until that task is completed.
|
||||
*/
|
||||
- (BFTask *)continueWithBlock:(BFContinuationBlock)block NS_SWIFT_NAME(continueWith(block:));
|
||||
|
||||
/*!
|
||||
Enqueues the given block to be run once this task is complete.
|
||||
This method uses a default execution strategy. The block will be
|
||||
run on the thread where the previous task completes, unless the
|
||||
the stack depth is too deep, in which case it will be run on a
|
||||
dispatch queue with default priority.
|
||||
@param block The block to be run once this task is complete.
|
||||
@param cancellationToken The cancellation token (optional).
|
||||
@returns A task that will be completed after block has run.
|
||||
If block returns a BFTask, then the task returned from
|
||||
this method will not be completed until that task is completed.
|
||||
*/
|
||||
- (BFTask *)continueWithBlock:(BFContinuationBlock)block
|
||||
cancellationToken:(nullable BFCancellationToken *)cancellationToken NS_SWIFT_NAME(continueWith(block:cancellationToken:));
|
||||
|
||||
/*!
|
||||
Enqueues the given block to be run once this task is complete.
|
||||
@param executor A BFExecutor responsible for determining how the
|
||||
continuation block will be run.
|
||||
@param block The block to be run once this task is complete.
|
||||
@returns A task that will be completed after block has run.
|
||||
If block returns a BFTask, then the task returned from
|
||||
this method will not be completed until that task is completed.
|
||||
*/
|
||||
- (BFTask *)continueWithExecutor:(BFExecutor *)executor
|
||||
withBlock:(BFContinuationBlock)block NS_SWIFT_NAME(continueWith(executor:block:));
|
||||
|
||||
/*!
|
||||
Enqueues the given block to be run once this task is complete.
|
||||
@param executor A BFExecutor responsible for determining how the
|
||||
continuation block will be run.
|
||||
@param block The block to be run once this task is complete.
|
||||
@param cancellationToken The cancellation token (optional).
|
||||
@returns A task that will be completed after block has run.
|
||||
If block returns a BFTask, then the task returned from
|
||||
his method will not be completed until that task is completed.
|
||||
*/
|
||||
- (BFTask *)continueWithExecutor:(BFExecutor *)executor
|
||||
block:(BFContinuationBlock)block
|
||||
cancellationToken:(nullable BFCancellationToken *)cancellationToken
|
||||
NS_SWIFT_NAME(continueWith(executor:block:cancellationToken:));
|
||||
|
||||
/*!
|
||||
Identical to continueWithBlock:, except that the block is only run
|
||||
if this task did not produce a cancellation or an error.
|
||||
If it did, then the failure will be propagated to the returned
|
||||
task.
|
||||
@param block The block to be run once this task is complete.
|
||||
@returns A task that will be completed after block has run.
|
||||
If block returns a BFTask, then the task returned from
|
||||
this method will not be completed until that task is completed.
|
||||
*/
|
||||
- (BFTask *)continueWithSuccessBlock:(BFContinuationBlock)block NS_SWIFT_NAME(continueOnSuccessWith(block:));
|
||||
|
||||
/*!
|
||||
Identical to continueWithBlock:, except that the block is only run
|
||||
if this task did not produce a cancellation or an error.
|
||||
If it did, then the failure will be propagated to the returned
|
||||
task.
|
||||
@param block The block to be run once this task is complete.
|
||||
@param cancellationToken The cancellation token (optional).
|
||||
@returns A task that will be completed after block has run.
|
||||
If block returns a BFTask, then the task returned from
|
||||
this method will not be completed until that task is completed.
|
||||
*/
|
||||
- (BFTask *)continueWithSuccessBlock:(BFContinuationBlock)block
|
||||
cancellationToken:(nullable BFCancellationToken *)cancellationToken
|
||||
NS_SWIFT_NAME(continueOnSuccessWith(block:cancellationToken:));
|
||||
|
||||
/*!
|
||||
Identical to continueWithExecutor:withBlock:, except that the block
|
||||
is only run if this task did not produce a cancellation, error, or an error.
|
||||
If it did, then the failure will be propagated to the returned task.
|
||||
@param executor A BFExecutor responsible for determining how the
|
||||
continuation block will be run.
|
||||
@param block The block to be run once this task is complete.
|
||||
@returns A task that will be completed after block has run.
|
||||
If block returns a BFTask, then the task returned from
|
||||
this method will not be completed until that task is completed.
|
||||
*/
|
||||
- (BFTask *)continueWithExecutor:(BFExecutor *)executor
|
||||
withSuccessBlock:(BFContinuationBlock)block NS_SWIFT_NAME(continueOnSuccessWith(executor:block:));
|
||||
|
||||
/*!
|
||||
Identical to continueWithExecutor:withBlock:, except that the block
|
||||
is only run if this task did not produce a cancellation or an error.
|
||||
If it did, then the failure will be propagated to the returned task.
|
||||
@param executor A BFExecutor responsible for determining how the
|
||||
continuation block will be run.
|
||||
@param block The block to be run once this task is complete.
|
||||
@param cancellationToken The cancellation token (optional).
|
||||
@returns A task that will be completed after block has run.
|
||||
If block returns a BFTask, then the task returned from
|
||||
this method will not be completed until that task is completed.
|
||||
*/
|
||||
- (BFTask *)continueWithExecutor:(BFExecutor *)executor
|
||||
successBlock:(BFContinuationBlock)block
|
||||
cancellationToken:(nullable BFCancellationToken *)cancellationToken
|
||||
NS_SWIFT_NAME(continueOnSuccessWith(executor:block:cancellationToken:));
|
||||
|
||||
/*!
|
||||
Waits until this operation is completed.
|
||||
This method is inefficient and consumes a thread resource while
|
||||
it's running. It should be avoided. This method logs a warning
|
||||
message if it is used on the main thread.
|
||||
*/
|
||||
- (void)waitUntilFinished;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
465
Example/Pods/Bolts/Bolts/Common/BFTask.m
generated
465
Example/Pods/Bolts/Bolts/Common/BFTask.m
generated
@ -1,465 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 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 "BFTask.h"
|
||||
|
||||
#import <libkern/OSAtomic.h>
|
||||
|
||||
#import "Bolts.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
__attribute__ ((noinline)) void warnBlockingOperationOnMainThread() {
|
||||
NSLog(@"Warning: A long-running operation is being executed on the main thread. \n"
|
||||
" Break on warnBlockingOperationOnMainThread() to debug.");
|
||||
}
|
||||
|
||||
NSString *const BFTaskErrorDomain = @"bolts";
|
||||
NSInteger const kBFMultipleErrorsError = 80175001;
|
||||
|
||||
NSString *const BFTaskMultipleErrorsUserInfoKey = @"errors";
|
||||
|
||||
@interface BFTask () {
|
||||
id _result;
|
||||
NSError *_error;
|
||||
}
|
||||
|
||||
@property (nonatomic, assign, readwrite, getter=isCancelled) BOOL cancelled;
|
||||
@property (nonatomic, assign, readwrite, getter=isFaulted) BOOL faulted;
|
||||
@property (nonatomic, assign, readwrite, getter=isCompleted) BOOL completed;
|
||||
|
||||
@property (nonatomic, strong) NSObject *lock;
|
||||
@property (nonatomic, strong) NSCondition *condition;
|
||||
@property (nonatomic, strong) NSMutableArray *callbacks;
|
||||
|
||||
@end
|
||||
|
||||
@implementation BFTask
|
||||
|
||||
#pragma mark - Initializer
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
if (!self) return self;
|
||||
|
||||
_lock = [[NSObject alloc] init];
|
||||
_condition = [[NSCondition alloc] init];
|
||||
_callbacks = [NSMutableArray array];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithResult:(nullable id)result {
|
||||
self = [super init];
|
||||
if (!self) return self;
|
||||
|
||||
[self trySetResult:result];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithError:(NSError *)error {
|
||||
self = [super init];
|
||||
if (!self) return self;
|
||||
|
||||
[self trySetError:error];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initCancelled {
|
||||
self = [super init];
|
||||
if (!self) return self;
|
||||
|
||||
[self trySetCancelled];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark - Task Class methods
|
||||
|
||||
+ (instancetype)taskWithResult:(nullable id)result {
|
||||
return [[self alloc] initWithResult:result];
|
||||
}
|
||||
|
||||
+ (instancetype)taskWithError:(NSError *)error {
|
||||
return [[self alloc] initWithError:error];
|
||||
}
|
||||
|
||||
+ (instancetype)cancelledTask {
|
||||
return [[self alloc] initCancelled];
|
||||
}
|
||||
|
||||
+ (instancetype)taskForCompletionOfAllTasks:(nullable NSArray<BFTask *> *)tasks {
|
||||
__block int32_t total = (int32_t)tasks.count;
|
||||
if (total == 0) {
|
||||
return [self taskWithResult:nil];
|
||||
}
|
||||
|
||||
__block int32_t cancelled = 0;
|
||||
NSObject *lock = [[NSObject alloc] init];
|
||||
NSMutableArray *errors = [NSMutableArray array];
|
||||
|
||||
BFTaskCompletionSource *tcs = [BFTaskCompletionSource taskCompletionSource];
|
||||
for (BFTask *task in tasks) {
|
||||
[task continueWithBlock:^id(BFTask *t) {
|
||||
if (t.error) {
|
||||
@synchronized (lock) {
|
||||
[errors addObject:t.error];
|
||||
}
|
||||
} else if (t.cancelled) {
|
||||
OSAtomicIncrement32Barrier(&cancelled);
|
||||
}
|
||||
|
||||
if (OSAtomicDecrement32Barrier(&total) == 0) {
|
||||
if (errors.count > 0) {
|
||||
if (errors.count == 1) {
|
||||
tcs.error = [errors firstObject];
|
||||
} else {
|
||||
NSError *error = [NSError errorWithDomain:BFTaskErrorDomain
|
||||
code:kBFMultipleErrorsError
|
||||
userInfo:@{ BFTaskMultipleErrorsUserInfoKey: errors }];
|
||||
tcs.error = error;
|
||||
}
|
||||
} else if (cancelled > 0) {
|
||||
[tcs cancel];
|
||||
} else {
|
||||
tcs.result = nil;
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
}];
|
||||
}
|
||||
return tcs.task;
|
||||
}
|
||||
|
||||
+ (instancetype)taskForCompletionOfAllTasksWithResults:(nullable NSArray<BFTask *> *)tasks {
|
||||
return [[self taskForCompletionOfAllTasks:tasks] continueWithSuccessBlock:^id(BFTask * __unused task) {
|
||||
return [tasks valueForKey:@"result"];
|
||||
}];
|
||||
}
|
||||
|
||||
+ (instancetype)taskForCompletionOfAnyTask:(nullable NSArray<BFTask *> *)tasks
|
||||
{
|
||||
__block int32_t total = (int32_t)tasks.count;
|
||||
if (total == 0) {
|
||||
return [self taskWithResult:nil];
|
||||
}
|
||||
|
||||
__block int completed = 0;
|
||||
__block int32_t cancelled = 0;
|
||||
|
||||
NSObject *lock = [NSObject new];
|
||||
NSMutableArray<NSError *> *errors = [NSMutableArray new];
|
||||
|
||||
BFTaskCompletionSource *source = [BFTaskCompletionSource taskCompletionSource];
|
||||
for (BFTask *task in tasks) {
|
||||
[task continueWithBlock:^id(BFTask *t) {
|
||||
if (t.error != nil) {
|
||||
@synchronized(lock) {
|
||||
[errors addObject:t.error];
|
||||
}
|
||||
} else if (t.cancelled) {
|
||||
OSAtomicIncrement32Barrier(&cancelled);
|
||||
} else {
|
||||
if(OSAtomicCompareAndSwap32Barrier(0, 1, &completed)) {
|
||||
[source setResult:t.result];
|
||||
}
|
||||
}
|
||||
|
||||
if (OSAtomicDecrement32Barrier(&total) == 0 &&
|
||||
OSAtomicCompareAndSwap32Barrier(0, 1, &completed)) {
|
||||
if (cancelled > 0) {
|
||||
[source cancel];
|
||||
} else if (errors.count > 0) {
|
||||
if (errors.count == 1) {
|
||||
source.error = errors.firstObject;
|
||||
} else {
|
||||
NSError *error = [NSError errorWithDomain:BFTaskErrorDomain
|
||||
code:kBFMultipleErrorsError
|
||||
userInfo:@{ @"errors": errors }];
|
||||
source.error = error;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Abort execution of per tasks continuations
|
||||
return nil;
|
||||
}];
|
||||
}
|
||||
return source.task;
|
||||
}
|
||||
|
||||
|
||||
+ (BFTask<BFVoid> *)taskWithDelay:(int)millis {
|
||||
BFTaskCompletionSource *tcs = [BFTaskCompletionSource taskCompletionSource];
|
||||
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, millis * NSEC_PER_MSEC);
|
||||
dispatch_after(popTime, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){
|
||||
tcs.result = nil;
|
||||
});
|
||||
return tcs.task;
|
||||
}
|
||||
|
||||
+ (BFTask<BFVoid> *)taskWithDelay:(int)millis cancellationToken:(nullable BFCancellationToken *)token {
|
||||
if (token.cancellationRequested) {
|
||||
return [BFTask cancelledTask];
|
||||
}
|
||||
|
||||
BFTaskCompletionSource *tcs = [BFTaskCompletionSource taskCompletionSource];
|
||||
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, millis * NSEC_PER_MSEC);
|
||||
dispatch_after(popTime, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){
|
||||
if (token.cancellationRequested) {
|
||||
[tcs cancel];
|
||||
return;
|
||||
}
|
||||
tcs.result = nil;
|
||||
});
|
||||
return tcs.task;
|
||||
}
|
||||
|
||||
+ (instancetype)taskFromExecutor:(BFExecutor *)executor withBlock:(nullable id (^)(void))block {
|
||||
return [[self taskWithResult:nil] continueWithExecutor:executor withBlock:^id(BFTask *task) {
|
||||
return block();
|
||||
}];
|
||||
}
|
||||
|
||||
#pragma mark - Custom Setters/Getters
|
||||
|
||||
- (nullable id)result {
|
||||
@synchronized(self.lock) {
|
||||
return _result;
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)trySetResult:(nullable id)result {
|
||||
@synchronized(self.lock) {
|
||||
if (self.completed) {
|
||||
return NO;
|
||||
}
|
||||
self.completed = YES;
|
||||
_result = result;
|
||||
[self runContinuations];
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
|
||||
- (nullable NSError *)error {
|
||||
@synchronized(self.lock) {
|
||||
return _error;
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)trySetError:(NSError *)error {
|
||||
@synchronized(self.lock) {
|
||||
if (self.completed) {
|
||||
return NO;
|
||||
}
|
||||
self.completed = YES;
|
||||
self.faulted = YES;
|
||||
_error = error;
|
||||
[self runContinuations];
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)isCancelled {
|
||||
@synchronized(self.lock) {
|
||||
return _cancelled;
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)isFaulted {
|
||||
@synchronized(self.lock) {
|
||||
return _faulted;
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)trySetCancelled {
|
||||
@synchronized(self.lock) {
|
||||
if (self.completed) {
|
||||
return NO;
|
||||
}
|
||||
self.completed = YES;
|
||||
self.cancelled = YES;
|
||||
[self runContinuations];
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)isCompleted {
|
||||
@synchronized(self.lock) {
|
||||
return _completed;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)runContinuations {
|
||||
@synchronized(self.lock) {
|
||||
[self.condition lock];
|
||||
[self.condition broadcast];
|
||||
[self.condition unlock];
|
||||
for (void (^callback)(void) in self.callbacks) {
|
||||
callback();
|
||||
}
|
||||
[self.callbacks removeAllObjects];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Chaining methods
|
||||
|
||||
- (BFTask *)continueWithExecutor:(BFExecutor *)executor withBlock:(BFContinuationBlock)block {
|
||||
return [self continueWithExecutor:executor block:block cancellationToken:nil];
|
||||
}
|
||||
|
||||
- (BFTask *)continueWithExecutor:(BFExecutor *)executor
|
||||
block:(BFContinuationBlock)block
|
||||
cancellationToken:(nullable BFCancellationToken *)cancellationToken {
|
||||
BFTaskCompletionSource *tcs = [BFTaskCompletionSource taskCompletionSource];
|
||||
|
||||
// Capture all of the state that needs to used when the continuation is complete.
|
||||
dispatch_block_t executionBlock = ^{
|
||||
if (cancellationToken.cancellationRequested) {
|
||||
[tcs cancel];
|
||||
return;
|
||||
}
|
||||
|
||||
id result = block(self);
|
||||
if ([result isKindOfClass:[BFTask class]]) {
|
||||
|
||||
id (^setupWithTask) (BFTask *) = ^id(BFTask *task) {
|
||||
if (cancellationToken.cancellationRequested || task.cancelled) {
|
||||
[tcs cancel];
|
||||
} else if (task.error) {
|
||||
tcs.error = task.error;
|
||||
} else {
|
||||
tcs.result = task.result;
|
||||
}
|
||||
return nil;
|
||||
};
|
||||
|
||||
BFTask *resultTask = (BFTask *)result;
|
||||
|
||||
if (resultTask.completed) {
|
||||
setupWithTask(resultTask);
|
||||
} else {
|
||||
[resultTask continueWithBlock:setupWithTask];
|
||||
}
|
||||
|
||||
} else {
|
||||
tcs.result = result;
|
||||
}
|
||||
};
|
||||
|
||||
BOOL completed;
|
||||
@synchronized(self.lock) {
|
||||
completed = self.completed;
|
||||
if (!completed) {
|
||||
[self.callbacks addObject:[^{
|
||||
[executor execute:executionBlock];
|
||||
} copy]];
|
||||
}
|
||||
}
|
||||
if (completed) {
|
||||
[executor execute:executionBlock];
|
||||
}
|
||||
|
||||
return tcs.task;
|
||||
}
|
||||
|
||||
- (BFTask *)continueWithBlock:(BFContinuationBlock)block {
|
||||
return [self continueWithExecutor:[BFExecutor defaultExecutor] block:block cancellationToken:nil];
|
||||
}
|
||||
|
||||
- (BFTask *)continueWithBlock:(BFContinuationBlock)block cancellationToken:(nullable BFCancellationToken *)cancellationToken {
|
||||
return [self continueWithExecutor:[BFExecutor defaultExecutor] block:block cancellationToken:cancellationToken];
|
||||
}
|
||||
|
||||
- (BFTask *)continueWithExecutor:(BFExecutor *)executor
|
||||
withSuccessBlock:(BFContinuationBlock)block {
|
||||
return [self continueWithExecutor:executor successBlock:block cancellationToken:nil];
|
||||
}
|
||||
|
||||
- (BFTask *)continueWithExecutor:(BFExecutor *)executor
|
||||
successBlock:(BFContinuationBlock)block
|
||||
cancellationToken:(nullable BFCancellationToken *)cancellationToken {
|
||||
if (cancellationToken.cancellationRequested) {
|
||||
return [BFTask cancelledTask];
|
||||
}
|
||||
|
||||
return [self continueWithExecutor:executor block:^id(BFTask *task) {
|
||||
if (task.faulted || task.cancelled) {
|
||||
return task;
|
||||
} else {
|
||||
return block(task);
|
||||
}
|
||||
} cancellationToken:cancellationToken];
|
||||
}
|
||||
|
||||
- (BFTask *)continueWithSuccessBlock:(BFContinuationBlock)block {
|
||||
return [self continueWithExecutor:[BFExecutor defaultExecutor] successBlock:block cancellationToken:nil];
|
||||
}
|
||||
|
||||
- (BFTask *)continueWithSuccessBlock:(BFContinuationBlock)block cancellationToken:(nullable BFCancellationToken *)cancellationToken {
|
||||
return [self continueWithExecutor:[BFExecutor defaultExecutor] successBlock:block cancellationToken:cancellationToken];
|
||||
}
|
||||
|
||||
#pragma mark - Syncing Task (Avoid it)
|
||||
|
||||
- (void)warnOperationOnMainThread {
|
||||
warnBlockingOperationOnMainThread();
|
||||
}
|
||||
|
||||
- (void)waitUntilFinished {
|
||||
if ([NSThread isMainThread]) {
|
||||
[self warnOperationOnMainThread];
|
||||
}
|
||||
|
||||
@synchronized(self.lock) {
|
||||
if (self.completed) {
|
||||
return;
|
||||
}
|
||||
[self.condition lock];
|
||||
}
|
||||
// TODO: (nlutsenko) Restructure this to use Bolts-Swift thread access synchronization architecture
|
||||
// In the meantime, it's absolutely safe to get `_completed` aka an ivar, as long as it's a `BOOL` aka less than word size.
|
||||
while (!_completed) {
|
||||
[self.condition wait];
|
||||
}
|
||||
[self.condition unlock];
|
||||
}
|
||||
|
||||
#pragma mark - NSObject
|
||||
|
||||
- (NSString *)description {
|
||||
// Acquire the data from the locked properties
|
||||
BOOL completed;
|
||||
BOOL cancelled;
|
||||
BOOL faulted;
|
||||
NSString *resultDescription = nil;
|
||||
|
||||
@synchronized(self.lock) {
|
||||
completed = self.completed;
|
||||
cancelled = self.cancelled;
|
||||
faulted = self.faulted;
|
||||
resultDescription = completed ? [NSString stringWithFormat:@" result = %@", self.result] : @"";
|
||||
}
|
||||
|
||||
// Description string includes status information and, if available, the
|
||||
// result since in some ways this is what a promise actually "is".
|
||||
return [NSString stringWithFormat:@"<%@: %p; completed = %@; cancelled = %@; faulted = %@;%@>",
|
||||
NSStringFromClass([self class]),
|
||||
self,
|
||||
completed ? @"YES" : @"NO",
|
||||
cancelled ? @"YES" : @"NO",
|
||||
faulted ? @"YES" : @"NO",
|
||||
resultDescription];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@ -1,75 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 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 <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class BFTask<__covariant ResultType>;
|
||||
|
||||
/*!
|
||||
A BFTaskCompletionSource represents the producer side of tasks.
|
||||
It is a task that also has methods for changing the state of the
|
||||
task by settings its completion values.
|
||||
*/
|
||||
@interface BFTaskCompletionSource<__covariant ResultType> : NSObject
|
||||
|
||||
/*!
|
||||
Creates a new unfinished task.
|
||||
*/
|
||||
+ (instancetype)taskCompletionSource;
|
||||
|
||||
/*!
|
||||
The task associated with this TaskCompletionSource.
|
||||
*/
|
||||
@property (nonatomic, strong, readonly) BFTask<ResultType> *task;
|
||||
|
||||
/*!
|
||||
Completes the task by setting the result.
|
||||
Attempting to set this for a completed task will raise an exception.
|
||||
@param result The result of the task.
|
||||
*/
|
||||
- (void)setResult:(nullable ResultType)result NS_SWIFT_NAME(set(result:));
|
||||
|
||||
/*!
|
||||
Completes the task by setting the error.
|
||||
Attempting to set this for a completed task will raise an exception.
|
||||
@param error The error for the task.
|
||||
*/
|
||||
- (void)setError:(NSError *)error NS_SWIFT_NAME(set(error:));
|
||||
|
||||
/*!
|
||||
Completes the task by marking it as cancelled.
|
||||
Attempting to set this for a completed task will raise an exception.
|
||||
*/
|
||||
- (void)cancel;
|
||||
|
||||
/*!
|
||||
Sets the result of the task if it wasn't already completed.
|
||||
@returns whether the new value was set.
|
||||
*/
|
||||
- (BOOL)trySetResult:(nullable ResultType)result NS_SWIFT_NAME(trySet(result:));
|
||||
|
||||
/*!
|
||||
Sets the error of the task if it wasn't already completed.
|
||||
@param error The error for the task.
|
||||
@returns whether the new value was set.
|
||||
*/
|
||||
- (BOOL)trySetError:(NSError *)error NS_SWIFT_NAME(trySet(error:));
|
||||
|
||||
/*!
|
||||
Sets the cancellation state of the task if it wasn't already completed.
|
||||
@returns whether the new value was set.
|
||||
*/
|
||||
- (BOOL)trySetCancelled;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@ -1,79 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 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 "BFTaskCompletionSource.h"
|
||||
|
||||
#import "BFTask.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface BFTask (BFTaskCompletionSource)
|
||||
|
||||
- (BOOL)trySetResult:(nullable id)result;
|
||||
- (BOOL)trySetError:(NSError *)error;
|
||||
- (BOOL)trySetCancelled;
|
||||
|
||||
@end
|
||||
|
||||
@implementation BFTaskCompletionSource
|
||||
|
||||
#pragma mark - Initializer
|
||||
|
||||
+ (instancetype)taskCompletionSource {
|
||||
return [[self alloc] init];
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
if (!self) return self;
|
||||
|
||||
_task = [[BFTask alloc] init];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark - Custom Setters/Getters
|
||||
|
||||
- (void)setResult:(nullable id)result {
|
||||
if (![self.task trySetResult:result]) {
|
||||
[NSException raise:NSInternalInconsistencyException
|
||||
format:@"Cannot set the result on a completed task."];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setError:(NSError *)error {
|
||||
if (![self.task trySetError:error]) {
|
||||
[NSException raise:NSInternalInconsistencyException
|
||||
format:@"Cannot set the error on a completed task."];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)cancel {
|
||||
if (![self.task trySetCancelled]) {
|
||||
[NSException raise:NSInternalInconsistencyException
|
||||
format:@"Cannot cancel a completed task."];
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)trySetResult:(nullable id)result {
|
||||
return [self.task trySetResult:result];
|
||||
}
|
||||
|
||||
- (BOOL)trySetError:(NSError *)error {
|
||||
return [self.task trySetError:error];
|
||||
}
|
||||
|
||||
- (BOOL)trySetCancelled {
|
||||
return [self.task trySetCancelled];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
39
Example/Pods/Bolts/Bolts/Common/Bolts.h
generated
39
Example/Pods/Bolts/Bolts/Common/Bolts.h
generated
@ -1,39 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 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 <Bolts/BFCancellationToken.h>
|
||||
#import <Bolts/BFCancellationTokenRegistration.h>
|
||||
#import <Bolts/BFCancellationTokenSource.h>
|
||||
#import <Bolts/BFExecutor.h>
|
||||
#import <Bolts/BFGeneric.h>
|
||||
#import <Bolts/BFTask.h>
|
||||
#import <Bolts/BFTaskCompletionSource.h>
|
||||
|
||||
#if __has_include(<Bolts/BFAppLink.h>) && TARGET_OS_IPHONE && !TARGET_OS_WATCH && !TARGET_OS_TV
|
||||
#import <Bolts/BFAppLink.h>
|
||||
#import <Bolts/BFAppLinkNavigation.h>
|
||||
#import <Bolts/BFAppLinkResolving.h>
|
||||
#import <Bolts/BFAppLinkReturnToRefererController.h>
|
||||
#import <Bolts/BFAppLinkReturnToRefererView.h>
|
||||
#import <Bolts/BFAppLinkTarget.h>
|
||||
#import <Bolts/BFMeasurementEvent.h>
|
||||
#import <Bolts/BFURL.h>
|
||||
#import <Bolts/BFWebViewAppLinkResolver.h>
|
||||
#endif
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
A string containing the version of the Bolts Framework used by the current application.
|
||||
*/
|
||||
extern NSString *const BoltsFrameworkVersionString;
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
17
Example/Pods/Bolts/Bolts/Common/Bolts.m
generated
17
Example/Pods/Bolts/Bolts/Common/Bolts.m
generated
@ -1,17 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 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 "Bolts.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
NSString *const BoltsFrameworkVersionString = @"1.9.0";
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
49
Example/Pods/Bolts/Bolts/iOS/BFAppLink.h
generated
49
Example/Pods/Bolts/Bolts/iOS/BFAppLink.h
generated
@ -1,49 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 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 <Foundation/Foundation.h>
|
||||
|
||||
/*! The version of the App Link protocol that this library supports */
|
||||
FOUNDATION_EXPORT NSString *const BFAppLinkVersion;
|
||||
|
||||
/*!
|
||||
Contains App Link metadata relevant for navigation on this device
|
||||
derived from the HTML at a given URL.
|
||||
*/
|
||||
@interface BFAppLink : NSObject
|
||||
|
||||
/*!
|
||||
Creates a BFAppLink with the given list of BFAppLinkTargets and target URL.
|
||||
|
||||
Generally, this will only be used by implementers of the BFAppLinkResolving protocol,
|
||||
as these implementers will produce App Link metadata for a given URL.
|
||||
|
||||
@param sourceURL the URL from which this App Link is derived
|
||||
@param targets an ordered list of BFAppLinkTargets for this platform derived
|
||||
from App Link metadata.
|
||||
@param webURL the fallback web URL, if any, for the app link.
|
||||
*/
|
||||
+ (instancetype)appLinkWithSourceURL:(NSURL *)sourceURL
|
||||
targets:(NSArray *)targets
|
||||
webURL:(NSURL *)webURL;
|
||||
|
||||
/*! The URL from which this BFAppLink was derived */
|
||||
@property (nonatomic, strong, readonly) NSURL *sourceURL;
|
||||
|
||||
/*!
|
||||
The ordered list of targets applicable to this platform that will be used
|
||||
for navigation.
|
||||
*/
|
||||
@property (nonatomic, copy, readonly) NSArray *targets;
|
||||
|
||||
/*! The fallback web URL to use if no targets are installed on this device. */
|
||||
@property (nonatomic, strong, readonly) NSURL *webURL;
|
||||
|
||||
@end
|
||||
62
Example/Pods/Bolts/Bolts/iOS/BFAppLink.m
generated
62
Example/Pods/Bolts/Bolts/iOS/BFAppLink.m
generated
@ -1,62 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 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 "BFAppLink_Internal.h"
|
||||
|
||||
NSString *const BFAppLinkDataParameterName = @"al_applink_data";
|
||||
NSString *const BFAppLinkTargetKeyName = @"target_url";
|
||||
NSString *const BFAppLinkUserAgentKeyName = @"user_agent";
|
||||
NSString *const BFAppLinkExtrasKeyName = @"extras";
|
||||
NSString *const BFAppLinkRefererAppLink = @"referer_app_link";
|
||||
NSString *const BFAppLinkRefererAppName = @"app_name";
|
||||
NSString *const BFAppLinkRefererUrl = @"url";
|
||||
NSString *const BFAppLinkVersionKeyName = @"version";
|
||||
NSString *const BFAppLinkVersion = @"1.0";
|
||||
|
||||
@interface BFAppLink ()
|
||||
|
||||
@property (nonatomic, strong, readwrite) NSURL *sourceURL;
|
||||
@property (nonatomic, copy, readwrite) NSArray *targets;
|
||||
@property (nonatomic, strong, readwrite) NSURL *webURL;
|
||||
|
||||
@property (nonatomic, assign, readwrite, getter=isBackToReferrer) BOOL backToReferrer;
|
||||
|
||||
@end
|
||||
|
||||
@implementation BFAppLink
|
||||
|
||||
+ (instancetype)appLinkWithSourceURL:(NSURL *)sourceURL
|
||||
targets:(NSArray *)targets
|
||||
webURL:(NSURL *)webURL
|
||||
isBackToReferrer:(BOOL)isBackToReferrer {
|
||||
BFAppLink *link = [[self alloc] initWithIsBackToReferrer:isBackToReferrer];
|
||||
link.sourceURL = sourceURL;
|
||||
link.targets = [targets copy];
|
||||
link.webURL = webURL;
|
||||
return link;
|
||||
}
|
||||
|
||||
+ (instancetype)appLinkWithSourceURL:(NSURL *)sourceURL
|
||||
targets:(NSArray *)targets
|
||||
webURL:(NSURL *)webURL {
|
||||
return [self appLinkWithSourceURL:sourceURL
|
||||
targets:targets
|
||||
webURL:webURL
|
||||
isBackToReferrer:NO];
|
||||
}
|
||||
|
||||
- (BFAppLink *)initWithIsBackToReferrer:(BOOL)backToReferrer {
|
||||
if ((self = [super init])) {
|
||||
_backToReferrer = backToReferrer;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
114
Example/Pods/Bolts/Bolts/iOS/BFAppLinkNavigation.h
generated
114
Example/Pods/Bolts/Bolts/iOS/BFAppLinkNavigation.h
generated
@ -1,114 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 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 <Foundation/Foundation.h>
|
||||
|
||||
#import <Bolts/BFAppLink.h>
|
||||
|
||||
/*!
|
||||
The result of calling navigate on a BFAppLinkNavigation
|
||||
*/
|
||||
typedef NS_ENUM(NSInteger, BFAppLinkNavigationType) {
|
||||
/*! Indicates that the navigation failed and no app was opened */
|
||||
BFAppLinkNavigationTypeFailure,
|
||||
/*! Indicates that the navigation succeeded by opening the URL in the browser */
|
||||
BFAppLinkNavigationTypeBrowser,
|
||||
/*! Indicates that the navigation succeeded by opening the URL in an app on the device */
|
||||
BFAppLinkNavigationTypeApp
|
||||
};
|
||||
|
||||
@protocol BFAppLinkResolving;
|
||||
@class BFTask;
|
||||
|
||||
/*!
|
||||
Represents a pending request to navigate to an App Link. Most developers will
|
||||
simply use navigateToURLInBackground: to open a URL, but developers can build
|
||||
custom requests with additional navigation and app data attached to them by
|
||||
creating BFAppLinkNavigations themselves.
|
||||
*/
|
||||
NS_EXTENSION_UNAVAILABLE_IOS("Not available in app extension")
|
||||
@interface BFAppLinkNavigation : NSObject
|
||||
|
||||
/*!
|
||||
The extras for the AppLinkNavigation. This will generally contain application-specific
|
||||
data that should be passed along with the request, such as advertiser or affiliate IDs or
|
||||
other such metadata relevant on this device.
|
||||
*/
|
||||
@property (nonatomic, copy, readonly) NSDictionary *extras;
|
||||
|
||||
/*!
|
||||
The al_applink_data for the AppLinkNavigation. This will generally contain data common to
|
||||
navigation attempts such as back-links, user agents, and other information that may be used
|
||||
in routing and handling an App Link request.
|
||||
*/
|
||||
@property (nonatomic, copy, readonly) NSDictionary *appLinkData;
|
||||
|
||||
/*! The AppLink to navigate to */
|
||||
@property (nonatomic, strong, readonly) BFAppLink *appLink;
|
||||
|
||||
/*! Creates an AppLinkNavigation with the given link, extras, and App Link data */
|
||||
+ (instancetype)navigationWithAppLink:(BFAppLink *)appLink
|
||||
extras:(NSDictionary *)extras
|
||||
appLinkData:(NSDictionary *)appLinkData;
|
||||
|
||||
/*!
|
||||
Creates an NSDictionary with the correct format for iOS callback URLs,
|
||||
to be used as 'appLinkData' argument in the call to navigationWithAppLink:extras:appLinkData:
|
||||
*/
|
||||
+ (NSDictionary *)callbackAppLinkDataForAppWithName:(NSString *)appName url:(NSString *)url;
|
||||
|
||||
/*! Performs the navigation */
|
||||
- (BFAppLinkNavigationType)navigate:(NSError **)error;
|
||||
|
||||
/*! Returns a BFAppLink for the given URL */
|
||||
+ (BFTask *)resolveAppLinkInBackground:(NSURL *)destination;
|
||||
|
||||
/*! Returns a BFAppLink for the given URL using the given App Link resolution strategy */
|
||||
+ (BFTask *)resolveAppLinkInBackground:(NSURL *)destination resolver:(id<BFAppLinkResolving>)resolver;
|
||||
|
||||
/*! Navigates to a BFAppLink and returns whether it opened in-app or in-browser */
|
||||
+ (BFAppLinkNavigationType)navigateToAppLink:(BFAppLink *)link error:(NSError **)error;
|
||||
|
||||
/*!
|
||||
Returns a BFAppLinkNavigationType based on a BFAppLink.
|
||||
It's essentially a no-side-effect version of navigateToAppLink:error:,
|
||||
allowing apps to determine flow based on the link type (e.g. open an
|
||||
internal web view instead of going straight to the browser for regular links.)
|
||||
*/
|
||||
+ (BFAppLinkNavigationType)navigationTypeForLink:(BFAppLink *)link;
|
||||
|
||||
/*!
|
||||
Return navigation type for current instance.
|
||||
No-side-effect version of navigate:
|
||||
*/
|
||||
- (BFAppLinkNavigationType)navigationType;
|
||||
|
||||
/*! Navigates to a URL (an asynchronous action) and returns a BFNavigationType */
|
||||
+ (BFTask *)navigateToURLInBackground:(NSURL *)destination;
|
||||
|
||||
/*!
|
||||
Navigates to a URL (an asynchronous action) using the given App Link resolution
|
||||
strategy and returns a BFNavigationType
|
||||
*/
|
||||
+ (BFTask *)navigateToURLInBackground:(NSURL *)destination resolver:(id<BFAppLinkResolving>)resolver;
|
||||
|
||||
/*!
|
||||
Gets the default resolver to be used for App Link resolution. If the developer has not set one explicitly,
|
||||
a basic, built-in resolver will be used.
|
||||
*/
|
||||
+ (id<BFAppLinkResolving>)defaultResolver;
|
||||
|
||||
/*!
|
||||
Sets the default resolver to be used for App Link resolution. Setting this to nil will revert the
|
||||
default resolver to the basic, built-in resolver provided by Bolts.
|
||||
*/
|
||||
+ (void)setDefaultResolver:(id<BFAppLinkResolving>)resolver;
|
||||
|
||||
@end
|
||||
284
Example/Pods/Bolts/Bolts/iOS/BFAppLinkNavigation.m
generated
284
Example/Pods/Bolts/Bolts/iOS/BFAppLinkNavigation.m
generated
@ -1,284 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 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 "BFAppLinkNavigation.h"
|
||||
|
||||
#import <Bolts/Bolts.h>
|
||||
|
||||
#import "BFMeasurementEvent_Internal.h"
|
||||
#import "BFAppLink_Internal.h"
|
||||
|
||||
FOUNDATION_EXPORT NSString *const BFAppLinkDataParameterName;
|
||||
FOUNDATION_EXPORT NSString *const BFAppLinkTargetKeyName;
|
||||
FOUNDATION_EXPORT NSString *const BFAppLinkUserAgentKeyName;
|
||||
FOUNDATION_EXPORT NSString *const BFAppLinkExtrasKeyName;
|
||||
FOUNDATION_EXPORT NSString *const BFAppLinkVersionKeyName;
|
||||
FOUNDATION_EXPORT NSString *const BFAppLinkRefererAppLink;
|
||||
FOUNDATION_EXPORT NSString *const BFAppLinkRefererAppName;
|
||||
FOUNDATION_EXPORT NSString *const BFAppLinkRefererUrl;
|
||||
|
||||
static id<BFAppLinkResolving> defaultResolver;
|
||||
|
||||
@interface BFAppLinkNavigation ()
|
||||
|
||||
@property (nonatomic, copy, readwrite) NSDictionary *extras;
|
||||
@property (nonatomic, copy, readwrite) NSDictionary *appLinkData;
|
||||
@property (nonatomic, strong, readwrite) BFAppLink *appLink;
|
||||
|
||||
@end
|
||||
|
||||
@implementation BFAppLinkNavigation
|
||||
|
||||
+ (instancetype)navigationWithAppLink:(BFAppLink *)appLink
|
||||
extras:(NSDictionary *)extras
|
||||
appLinkData:(NSDictionary *)appLinkData {
|
||||
BFAppLinkNavigation *navigation = [[self alloc] init];
|
||||
navigation.appLink = appLink;
|
||||
navigation.extras = extras;
|
||||
navigation.appLinkData = appLinkData;
|
||||
return navigation;
|
||||
}
|
||||
|
||||
+ (NSDictionary *)callbackAppLinkDataForAppWithName:(NSString *)appName url:(NSString *)url {
|
||||
return @{BFAppLinkRefererAppLink: @{BFAppLinkRefererAppName: appName, BFAppLinkRefererUrl: url}};
|
||||
}
|
||||
|
||||
- (NSString *)stringByEscapingQueryString:(NSString *)string {
|
||||
#if __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_7_0 || __MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_9
|
||||
return [string stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
|
||||
#else
|
||||
return (NSString *)CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(NULL,
|
||||
(CFStringRef)string,
|
||||
NULL,
|
||||
(CFStringRef) @":/?#[]@!$&'()*+,;=",
|
||||
kCFStringEncodingUTF8));
|
||||
#endif
|
||||
}
|
||||
|
||||
- (NSURL *)appLinkURLWithTargetURL:(NSURL *)targetUrl error:(NSError **)error {
|
||||
NSMutableDictionary *appLinkData = [NSMutableDictionary dictionaryWithDictionary:self.appLinkData ?: @{}];
|
||||
|
||||
// Add applink protocol data
|
||||
if (!appLinkData[BFAppLinkUserAgentKeyName]) {
|
||||
appLinkData[BFAppLinkUserAgentKeyName] = [NSString stringWithFormat:@"Bolts iOS %@", BoltsFrameworkVersionString];
|
||||
}
|
||||
if (!appLinkData[BFAppLinkVersionKeyName]) {
|
||||
appLinkData[BFAppLinkVersionKeyName] = BFAppLinkVersion;
|
||||
}
|
||||
appLinkData[BFAppLinkTargetKeyName] = [self.appLink.sourceURL absoluteString];
|
||||
appLinkData[BFAppLinkExtrasKeyName] = self.extras ?: @{};
|
||||
|
||||
// JSON-ify the applink data
|
||||
NSError *jsonError = nil;
|
||||
NSData *jsonBlob = [NSJSONSerialization dataWithJSONObject:appLinkData options:0 error:&jsonError];
|
||||
if (!jsonError) {
|
||||
NSString *jsonString = [[NSString alloc] initWithData:jsonBlob encoding:NSUTF8StringEncoding];
|
||||
NSString *encoded = [self stringByEscapingQueryString:jsonString];
|
||||
|
||||
NSString *endUrlString = [NSString stringWithFormat:@"%@%@%@=%@",
|
||||
[targetUrl absoluteString],
|
||||
targetUrl.query ? @"&" : @"?",
|
||||
BFAppLinkDataParameterName,
|
||||
encoded];
|
||||
|
||||
return [NSURL URLWithString:endUrlString];
|
||||
} else {
|
||||
if (error) {
|
||||
*error = jsonError;
|
||||
}
|
||||
|
||||
// If there was an error encoding the app link data, fail hard.
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (BFAppLinkNavigationType)navigate:(NSError **)error {
|
||||
NSURL *openedURL = nil;
|
||||
NSError *encodingError = nil;
|
||||
BFAppLinkNavigationType retType = BFAppLinkNavigationTypeFailure;
|
||||
|
||||
// Find the first eligible/launchable target in the BFAppLink.
|
||||
for (BFAppLinkTarget *target in self.appLink.targets) {
|
||||
NSURL *appLinkAppURL = [self appLinkURLWithTargetURL:target.URL error:&encodingError];
|
||||
if (encodingError || !appLinkAppURL) {
|
||||
if (error) {
|
||||
*error = encodingError;
|
||||
}
|
||||
} else if ([[UIApplication sharedApplication] openURL:appLinkAppURL]) {
|
||||
retType = BFAppLinkNavigationTypeApp;
|
||||
openedURL = appLinkAppURL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!openedURL && self.appLink.webURL) {
|
||||
// Fall back to opening the url in the browser if available.
|
||||
NSURL *appLinkBrowserURL = [self appLinkURLWithTargetURL:self.appLink.webURL error:&encodingError];
|
||||
if (encodingError || !appLinkBrowserURL) {
|
||||
// If there was an error encoding the app link data, fail hard.
|
||||
if (error) {
|
||||
*error = encodingError;
|
||||
}
|
||||
} else if ([[UIApplication sharedApplication] openURL:appLinkBrowserURL]) {
|
||||
// This was a browser navigation.
|
||||
retType = BFAppLinkNavigationTypeBrowser;
|
||||
openedURL = appLinkBrowserURL;
|
||||
}
|
||||
}
|
||||
|
||||
[self postAppLinkNavigateEventNotificationWithTargetURL:openedURL
|
||||
error:error ? *error : nil
|
||||
type:retType];
|
||||
return retType;
|
||||
}
|
||||
|
||||
- (void)postAppLinkNavigateEventNotificationWithTargetURL:(NSURL *)outputURL error:(NSError *)error type:(BFAppLinkNavigationType)type {
|
||||
NSString *const EVENT_YES_VAL = @"1";
|
||||
NSString *const EVENT_NO_VAL = @"0";
|
||||
NSMutableDictionary *logData = [[NSMutableDictionary alloc] init];
|
||||
|
||||
NSString *outputURLScheme = [outputURL scheme];
|
||||
NSString *outputURLString = [outputURL absoluteString];
|
||||
if (outputURLScheme) {
|
||||
logData[@"outputURLScheme"] = outputURLScheme;
|
||||
}
|
||||
if (outputURLString) {
|
||||
logData[@"outputURL"] = outputURLString;
|
||||
}
|
||||
|
||||
NSString *sourceURLString = [self.appLink.sourceURL absoluteString];
|
||||
NSString *sourceURLHost = [self.appLink.sourceURL host];
|
||||
NSString *sourceURLScheme = [self.appLink.sourceURL scheme];
|
||||
if (sourceURLString) {
|
||||
logData[@"sourceURL"] = sourceURLString;
|
||||
}
|
||||
if (sourceURLHost) {
|
||||
logData[@"sourceHost"] = sourceURLHost;
|
||||
}
|
||||
if (sourceURLScheme) {
|
||||
logData[@"sourceScheme"] = sourceURLScheme;
|
||||
}
|
||||
if ([error localizedDescription]) {
|
||||
logData[@"error"] = [error localizedDescription];
|
||||
}
|
||||
NSString *success = nil; //no
|
||||
NSString *linkType = nil; // unknown;
|
||||
switch (type) {
|
||||
case BFAppLinkNavigationTypeFailure:
|
||||
success = EVENT_NO_VAL;
|
||||
linkType = @"fail";
|
||||
break;
|
||||
case BFAppLinkNavigationTypeBrowser:
|
||||
success = EVENT_YES_VAL;
|
||||
linkType = @"web";
|
||||
break;
|
||||
case BFAppLinkNavigationTypeApp:
|
||||
success = EVENT_YES_VAL;
|
||||
linkType = @"app";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (success) {
|
||||
logData[@"success"] = success;
|
||||
}
|
||||
if (linkType) {
|
||||
logData[@"type"] = linkType;
|
||||
}
|
||||
|
||||
if ([self.appLink isBackToReferrer]) {
|
||||
[BFMeasurementEvent postNotificationForEventName:BFAppLinkNavigateBackToReferrerEventName args:logData];
|
||||
} else {
|
||||
[BFMeasurementEvent postNotificationForEventName:BFAppLinkNavigateOutEventName args:logData];
|
||||
}
|
||||
}
|
||||
|
||||
+ (BFTask *)resolveAppLinkInBackground:(NSURL *)destination resolver:(id<BFAppLinkResolving>)resolver {
|
||||
return [resolver appLinkFromURLInBackground:destination];
|
||||
}
|
||||
|
||||
+ (BFTask *)resolveAppLinkInBackground:(NSURL *)destination {
|
||||
return [self resolveAppLinkInBackground:destination resolver:[self defaultResolver]];
|
||||
}
|
||||
|
||||
+ (BFTask *)navigateToURLInBackground:(NSURL *)destination {
|
||||
return [self navigateToURLInBackground:destination
|
||||
resolver:[self defaultResolver]];
|
||||
}
|
||||
|
||||
+ (BFTask *)navigateToURLInBackground:(NSURL *)destination
|
||||
resolver:(id<BFAppLinkResolving>)resolver {
|
||||
BFTask *resolutionTask = [self resolveAppLinkInBackground:destination
|
||||
resolver:resolver];
|
||||
return [resolutionTask continueWithExecutor:[BFExecutor mainThreadExecutor]
|
||||
withSuccessBlock:^id(BFTask *task) {
|
||||
NSError *error = nil;
|
||||
BFAppLinkNavigationType result = [self navigateToAppLink:task.result
|
||||
error:&error];
|
||||
if (error) {
|
||||
return [BFTask taskWithError:error];
|
||||
} else {
|
||||
return @(result);
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
+ (BFAppLinkNavigationType)navigateToAppLink:(BFAppLink *)link error:(NSError **)error {
|
||||
return [[BFAppLinkNavigation navigationWithAppLink:link
|
||||
extras:nil
|
||||
appLinkData:nil] navigate:error];
|
||||
}
|
||||
|
||||
+ (BFAppLinkNavigationType)navigationTypeForLink:(BFAppLink *)link {
|
||||
return [[self navigationWithAppLink:link extras:nil appLinkData:nil] navigationType];
|
||||
}
|
||||
|
||||
- (BFAppLinkNavigationType)navigationType {
|
||||
BFAppLinkTarget *eligibleTarget = nil;
|
||||
for (BFAppLinkTarget *target in self.appLink.targets) {
|
||||
if ([[UIApplication sharedApplication] canOpenURL:target.URL]) {
|
||||
eligibleTarget = target;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (eligibleTarget != nil) {
|
||||
NSURL *appLinkURL = [self appLinkURLWithTargetURL:eligibleTarget.URL error:nil];
|
||||
if (appLinkURL != nil) {
|
||||
return BFAppLinkNavigationTypeApp;
|
||||
} else {
|
||||
return BFAppLinkNavigationTypeFailure;
|
||||
}
|
||||
}
|
||||
|
||||
if (self.appLink.webURL != nil) {
|
||||
NSURL *appLinkURL = [self appLinkURLWithTargetURL:eligibleTarget.URL error:nil];
|
||||
if (appLinkURL != nil) {
|
||||
return BFAppLinkNavigationTypeBrowser;
|
||||
} else {
|
||||
return BFAppLinkNavigationTypeFailure;
|
||||
}
|
||||
}
|
||||
|
||||
return BFAppLinkNavigationTypeFailure;
|
||||
}
|
||||
|
||||
+ (id<BFAppLinkResolving>)defaultResolver {
|
||||
if (defaultResolver) {
|
||||
return defaultResolver;
|
||||
}
|
||||
return [BFWebViewAppLinkResolver sharedInstance];
|
||||
}
|
||||
|
||||
+ (void)setDefaultResolver:(id<BFAppLinkResolving>)resolver {
|
||||
defaultResolver = resolver;
|
||||
}
|
||||
|
||||
@end
|
||||
30
Example/Pods/Bolts/Bolts/iOS/BFAppLinkResolving.h
generated
30
Example/Pods/Bolts/Bolts/iOS/BFAppLinkResolving.h
generated
@ -1,30 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 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 <Foundation/Foundation.h>
|
||||
|
||||
@class BFTask;
|
||||
|
||||
/*!
|
||||
Implement this protocol to provide an alternate strategy for resolving
|
||||
App Links that may include pre-fetching, caching, or querying for App Link
|
||||
data from an index provided by a service provider.
|
||||
*/
|
||||
@protocol BFAppLinkResolving <NSObject>
|
||||
|
||||
/*!
|
||||
Asynchronously resolves App Link data for a given URL.
|
||||
|
||||
@param url The URL to resolve into an App Link.
|
||||
@returns A BFTask that will return a BFAppLink for the given URL.
|
||||
*/
|
||||
- (BFTask *)appLinkFromURLInBackground:(NSURL *)url NS_EXTENSION_UNAVAILABLE_IOS("Not available in app extension");
|
||||
|
||||
@end
|
||||
@ -1,88 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 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 <Foundation/Foundation.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import <Bolts/BFAppLinkReturnToRefererView.h>
|
||||
|
||||
@class BFAppLink;
|
||||
@class BFAppLinkReturnToRefererController;
|
||||
|
||||
/*!
|
||||
Protocol that a class can implement in order to be notified when the user has navigated back
|
||||
to the referer of an App Link.
|
||||
*/
|
||||
@protocol BFAppLinkReturnToRefererControllerDelegate <NSObject>
|
||||
|
||||
@optional
|
||||
|
||||
/*! Called when the user has tapped to navigate, but before the navigation has been performed. */
|
||||
- (void)returnToRefererController:(BFAppLinkReturnToRefererController *)controller
|
||||
willNavigateToAppLink:(BFAppLink *)appLink;
|
||||
|
||||
/*! Called after the navigation has been attempted, with an indication of whether the referer
|
||||
app link was successfully opened. */
|
||||
- (void)returnToRefererController:(BFAppLinkReturnToRefererController *)controller
|
||||
didNavigateToAppLink:(BFAppLink *)url
|
||||
type:(BFAppLinkNavigationType)type;
|
||||
|
||||
@end
|
||||
|
||||
/*!
|
||||
A controller class that implements default behavior for a BFAppLinkReturnToRefererView, including
|
||||
the ability to display the view above the navigation bar for navigation-based apps.
|
||||
*/
|
||||
NS_EXTENSION_UNAVAILABLE_IOS("Not available in app extension")
|
||||
@interface BFAppLinkReturnToRefererController : NSObject <BFAppLinkReturnToRefererViewDelegate>
|
||||
|
||||
/*!
|
||||
The delegate that will be notified when the user navigates back to the referer.
|
||||
*/
|
||||
@property (nonatomic, weak) id<BFAppLinkReturnToRefererControllerDelegate> delegate;
|
||||
|
||||
/*!
|
||||
The BFAppLinkReturnToRefererView this controller is controlling.
|
||||
*/
|
||||
@property (nonatomic, strong) BFAppLinkReturnToRefererView *view;
|
||||
|
||||
/*!
|
||||
Initializes a controller suitable for controlling a BFAppLinkReturnToRefererView that is to be displayed
|
||||
contained within another UIView (i.e., not displayed above the navigation bar).
|
||||
*/
|
||||
- (instancetype)init;
|
||||
|
||||
/*!
|
||||
Initializes a controller suitable for controlling a BFAppLinkReturnToRefererView that is to be displayed
|
||||
displayed above the navigation bar.
|
||||
*/
|
||||
- (instancetype)initForDisplayAboveNavController:(UINavigationController *)navController;
|
||||
|
||||
/*!
|
||||
Removes the view entirely from the navigation controller it is currently displayed in.
|
||||
*/
|
||||
- (void)removeFromNavController;
|
||||
|
||||
/*!
|
||||
Shows the BFAppLinkReturnToRefererView with the specified referer information. If nil or missing data,
|
||||
the view will not be displayed. */
|
||||
- (void)showViewForRefererAppLink:(BFAppLink *)refererAppLink;
|
||||
|
||||
/*!
|
||||
Shows the BFAppLinkReturnToRefererView with referer information extracted from the specified URL.
|
||||
If nil or missing referer App Link data, the view will not be displayed. */
|
||||
- (void)showViewForRefererURL:(NSURL *)url;
|
||||
|
||||
/*!
|
||||
Closes the view, possibly animating it.
|
||||
*/
|
||||
- (void)closeViewAnimated:(BOOL)animated;
|
||||
|
||||
@end
|
||||
@ -1,78 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 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 <Foundation/Foundation.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import <Bolts/BFAppLinkNavigation.h>
|
||||
|
||||
@class BFAppLinkReturnToRefererView;
|
||||
@class BFURL;
|
||||
|
||||
typedef NS_ENUM(NSUInteger, BFIncludeStatusBarInSize) {
|
||||
BFIncludeStatusBarInSizeNever,
|
||||
BFIncludeStatusBarInSizeIOS7AndLater,
|
||||
BFIncludeStatusBarInSizeAlways,
|
||||
};
|
||||
|
||||
/*!
|
||||
Protocol that a class can implement in order to be notified when the user has navigated back
|
||||
to the referer of an App Link.
|
||||
*/
|
||||
@protocol BFAppLinkReturnToRefererViewDelegate <NSObject>
|
||||
|
||||
/*!
|
||||
Called when the user has tapped inside the close button.
|
||||
*/
|
||||
- (void)returnToRefererViewDidTapInsideCloseButton:(BFAppLinkReturnToRefererView *)view;
|
||||
|
||||
/*!
|
||||
Called when the user has tapped inside the App Link portion of the view.
|
||||
*/
|
||||
- (void)returnToRefererViewDidTapInsideLink:(BFAppLinkReturnToRefererView *)view
|
||||
link:(BFAppLink *)link;
|
||||
|
||||
@end
|
||||
|
||||
/*!
|
||||
Provides a UIView that displays a button allowing users to navigate back to the
|
||||
application that launched the App Link currently being handled, if the App Link
|
||||
contained referer data. The user can also close the view by clicking a close button
|
||||
rather than navigating away. If the view is provided an App Link that does not contain
|
||||
referer data, it will have zero size and no UI will be displayed.
|
||||
*/
|
||||
NS_EXTENSION_UNAVAILABLE_IOS("Not available in app extension")
|
||||
@interface BFAppLinkReturnToRefererView : UIView
|
||||
|
||||
/*!
|
||||
The delegate that will be notified when the user navigates back to the referer.
|
||||
*/
|
||||
@property (nonatomic, weak) id<BFAppLinkReturnToRefererViewDelegate> delegate;
|
||||
|
||||
/*!
|
||||
The color of the text label and close button.
|
||||
*/
|
||||
@property (nonatomic, strong) UIColor *textColor;
|
||||
|
||||
@property (nonatomic, strong) BFAppLink *refererAppLink;
|
||||
|
||||
/*!
|
||||
Indicates whether to extend the size of the view to include the current status bar
|
||||
size, for use in scenarios where the view might extend under the status bar on iOS 7 and
|
||||
above; this property has no effect on earlier versions of iOS.
|
||||
*/
|
||||
@property (nonatomic, assign) BFIncludeStatusBarInSize includeStatusBarInSize;
|
||||
|
||||
/*!
|
||||
Indicates whether the user has closed the view by clicking the close button.
|
||||
*/
|
||||
@property (nonatomic, assign) BOOL closed;
|
||||
|
||||
@end
|
||||
33
Example/Pods/Bolts/Bolts/iOS/BFAppLinkTarget.h
generated
33
Example/Pods/Bolts/Bolts/iOS/BFAppLinkTarget.h
generated
@ -1,33 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 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 <Foundation/Foundation.h>
|
||||
|
||||
/*!
|
||||
Represents a target defined in App Link metadata, consisting of at least
|
||||
a URL, and optionally an App Store ID and name.
|
||||
*/
|
||||
@interface BFAppLinkTarget : NSObject
|
||||
|
||||
/*! Creates a BFAppLinkTarget with the given app site and target URL. */
|
||||
+ (instancetype)appLinkTargetWithURL:(NSURL *)url
|
||||
appStoreId:(NSString *)appStoreId
|
||||
appName:(NSString *)appName;
|
||||
|
||||
/*! The URL prefix for this app link target */
|
||||
@property (nonatomic, strong, readonly) NSURL *URL;
|
||||
|
||||
/*! The app ID for the app store */
|
||||
@property (nonatomic, copy, readonly) NSString *appStoreId;
|
||||
|
||||
/*! The name of the app */
|
||||
@property (nonatomic, copy, readonly) NSString *appName;
|
||||
|
||||
@end
|
||||
33
Example/Pods/Bolts/Bolts/iOS/BFAppLinkTarget.m
generated
33
Example/Pods/Bolts/Bolts/iOS/BFAppLinkTarget.m
generated
@ -1,33 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 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 "BFAppLinkTarget.h"
|
||||
|
||||
@interface BFAppLinkTarget ()
|
||||
|
||||
@property (nonatomic, strong, readwrite) NSURL *URL;
|
||||
@property (nonatomic, copy, readwrite) NSString *appStoreId;
|
||||
@property (nonatomic, copy, readwrite) NSString *appName;
|
||||
|
||||
@end
|
||||
|
||||
@implementation BFAppLinkTarget
|
||||
|
||||
+ (instancetype)appLinkTargetWithURL:(NSURL *)url
|
||||
appStoreId:(NSString *)appStoreId
|
||||
appName:(NSString *)appName {
|
||||
BFAppLinkTarget *target = [[self alloc] init];
|
||||
target.URL = url;
|
||||
target.appStoreId = appStoreId;
|
||||
target.appName = appName;
|
||||
return target;
|
||||
}
|
||||
|
||||
@end
|
||||
45
Example/Pods/Bolts/Bolts/iOS/BFMeasurementEvent.h
generated
45
Example/Pods/Bolts/Bolts/iOS/BFMeasurementEvent.h
generated
@ -1,45 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 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 <Foundation/Foundation.h>
|
||||
|
||||
/*! The name of the notification posted by BFMeasurementEvent */
|
||||
FOUNDATION_EXPORT NSString *const BFMeasurementEventNotificationName;
|
||||
|
||||
/*! Defines keys in the userInfo object for the notification named BFMeasurementEventNotificationName */
|
||||
/*! The string field for the name of the event */
|
||||
FOUNDATION_EXPORT NSString *const BFMeasurementEventNameKey;
|
||||
/*! The dictionary field for the arguments of the event */
|
||||
FOUNDATION_EXPORT NSString *const BFMeasurementEventArgsKey;
|
||||
|
||||
/*! Bolts Events raised by BFMeasurementEvent for Applink */
|
||||
/*!
|
||||
The name of the event posted when [BFURL URLWithURL:] is called successfully. This represents the successful parsing of an app link URL.
|
||||
*/
|
||||
FOUNDATION_EXPORT NSString *const BFAppLinkParseEventName;
|
||||
|
||||
/*!
|
||||
The name of the event posted when [BFURL URLWithInboundURL:] is called successfully.
|
||||
This represents parsing an inbound app link URL from a different application
|
||||
*/
|
||||
FOUNDATION_EXPORT NSString *const BFAppLinkNavigateInEventName;
|
||||
|
||||
/*! The event raised when the user navigates from your app to other apps */
|
||||
FOUNDATION_EXPORT NSString *const BFAppLinkNavigateOutEventName;
|
||||
|
||||
/*!
|
||||
The event raised when the user navigates out from your app and back to the referrer app.
|
||||
e.g when the user leaves your app after tapping the back-to-referrer navigation bar
|
||||
*/
|
||||
FOUNDATION_EXPORT NSString *const BFAppLinkNavigateBackToReferrerEventName;
|
||||
|
||||
@interface BFMeasurementEvent : NSObject
|
||||
|
||||
@end
|
||||
62
Example/Pods/Bolts/Bolts/iOS/BFMeasurementEvent.m
generated
62
Example/Pods/Bolts/Bolts/iOS/BFMeasurementEvent.m
generated
@ -1,62 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 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 "BFMeasurementEvent_Internal.h"
|
||||
|
||||
NSString *const BFMeasurementEventNotificationName = @"com.parse.bolts.measurement_event";
|
||||
|
||||
NSString *const BFMeasurementEventNameKey = @"event_name";
|
||||
NSString *const BFMeasurementEventArgsKey = @"event_args";
|
||||
|
||||
/* app Link Event raised by this BFURL */
|
||||
NSString *const BFAppLinkParseEventName = @"al_link_parse";
|
||||
NSString *const BFAppLinkNavigateInEventName = @"al_nav_in";
|
||||
|
||||
/*! AppLink events raised in this class */
|
||||
NSString *const BFAppLinkNavigateOutEventName = @"al_nav_out";
|
||||
NSString *const BFAppLinkNavigateBackToReferrerEventName = @"al_ref_back_out";
|
||||
|
||||
__attribute__((noinline)) void warnOnMissingEventName() {
|
||||
NSLog(@"Warning: Missing event name when logging bolts measurement event. \n"
|
||||
" Ignoring this event in logging.");
|
||||
}
|
||||
|
||||
@implementation BFMeasurementEvent {
|
||||
NSString *_name;
|
||||
NSDictionary *_args;
|
||||
}
|
||||
|
||||
- (void)postNotification {
|
||||
if (!_name) {
|
||||
warnOnMissingEventName();
|
||||
return;
|
||||
}
|
||||
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
|
||||
NSDictionary *userInfo = @{BFMeasurementEventNameKey : _name,
|
||||
BFMeasurementEventArgsKey : _args};
|
||||
|
||||
[center postNotificationName:BFMeasurementEventNotificationName
|
||||
object:self
|
||||
userInfo:userInfo];
|
||||
}
|
||||
|
||||
- (instancetype)initEventWithName:(NSString *)name args:(NSDictionary *)args {
|
||||
if ((self = [super init])) {
|
||||
_name = name;
|
||||
_args = args ? args : @{};
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
+ (void)postNotificationForEventName:(NSString *)name args:(NSDictionary *)args {
|
||||
[[[self alloc] initEventWithName:name args:args] postNotification];
|
||||
}
|
||||
|
||||
@end
|
||||
75
Example/Pods/Bolts/Bolts/iOS/BFURL.h
generated
75
Example/Pods/Bolts/Bolts/iOS/BFURL.h
generated
@ -1,75 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 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 <Foundation/Foundation.h>
|
||||
|
||||
@class BFAppLink;
|
||||
|
||||
/*!
|
||||
Provides a set of utilities for working with NSURLs, such as parsing of query parameters
|
||||
and handling for App Link requests.
|
||||
*/
|
||||
@interface BFURL : NSObject
|
||||
|
||||
/*!
|
||||
Creates a link target from a raw URL.
|
||||
On success, this posts the BFAppLinkParseEventName measurement event. If you are constructing the BFURL within your application delegate's
|
||||
application:openURL:sourceApplication:annotation:, you should instead use URLWithInboundURL:sourceApplication:
|
||||
to support better BFMeasurementEvent notifications
|
||||
@param url The instance of `NSURL` to create BFURL from.
|
||||
*/
|
||||
+ (BFURL *)URLWithURL:(NSURL *)url;
|
||||
|
||||
/*!
|
||||
Creates a link target from a raw URL received from an external application. This is typically called from the app delegate's
|
||||
application:openURL:sourceApplication:annotation: and will post the BFAppLinkNavigateInEventName measurement event.
|
||||
@param url The instance of `NSURL` to create BFURL from.
|
||||
@param sourceApplication the bundle ID of the app that is requesting your app to open the URL. The same sourceApplication in application:openURL:sourceApplication:annotation:
|
||||
*/
|
||||
+ (BFURL *)URLWithInboundURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication;
|
||||
|
||||
/*!
|
||||
Gets the target URL. If the link is an App Link, this is the target of the App Link.
|
||||
Otherwise, it is the url that created the target.
|
||||
*/
|
||||
@property (nonatomic, strong, readonly) NSURL *targetURL;
|
||||
|
||||
/*!
|
||||
Gets the query parameters for the target, parsed into an NSDictionary.
|
||||
*/
|
||||
@property (nonatomic, strong, readonly) NSDictionary *targetQueryParameters;
|
||||
|
||||
/*!
|
||||
If this link target is an App Link, this is the data found in al_applink_data.
|
||||
Otherwise, it is nil.
|
||||
*/
|
||||
@property (nonatomic, strong, readonly) NSDictionary *appLinkData;
|
||||
|
||||
/*!
|
||||
If this link target is an App Link, this is the data found in extras.
|
||||
*/
|
||||
@property (nonatomic, strong, readonly) NSDictionary *appLinkExtras;
|
||||
|
||||
/*!
|
||||
The App Link indicating how to navigate back to the referer app, if any.
|
||||
*/
|
||||
@property (nonatomic, strong, readonly) BFAppLink *appLinkReferer;
|
||||
|
||||
/*!
|
||||
The URL that was used to create this BFURL.
|
||||
*/
|
||||
@property (nonatomic, strong, readonly) NSURL *inputURL;
|
||||
|
||||
/*!
|
||||
The query parameters of the inputURL, parsed into an NSDictionary.
|
||||
*/
|
||||
@property (nonatomic, strong, readonly) NSDictionary *inputQueryParameters;
|
||||
|
||||
@end
|
||||
142
Example/Pods/Bolts/Bolts/iOS/BFURL.m
generated
142
Example/Pods/Bolts/Bolts/iOS/BFURL.m
generated
@ -1,142 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 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 "BFURL_Internal.h"
|
||||
#import "BFAppLink_Internal.h"
|
||||
#import "BFAppLinkTarget.h"
|
||||
#import "BFMeasurementEvent_Internal.h"
|
||||
|
||||
@implementation BFURL
|
||||
|
||||
- (instancetype)initWithURL:(NSURL *)url forOpenInboundURL:(BOOL)forOpenURLEvent sourceApplication:(NSString *)sourceApplication forRenderBackToReferrerBar:(BOOL)forRenderBackToReferrerBar {
|
||||
self = [super init];
|
||||
if (!self) return nil;
|
||||
|
||||
_inputURL = url;
|
||||
_targetURL = url;
|
||||
|
||||
// Parse the query string parameters for the base URL
|
||||
NSDictionary *baseQuery = [BFURL queryParametersForURL:url];
|
||||
_inputQueryParameters = baseQuery;
|
||||
_targetQueryParameters = baseQuery;
|
||||
|
||||
// Check for applink_data
|
||||
NSString *appLinkDataString = baseQuery[BFAppLinkDataParameterName];
|
||||
if (appLinkDataString) {
|
||||
// Try to parse the JSON
|
||||
NSError *error = nil;
|
||||
NSDictionary *applinkData = [NSJSONSerialization JSONObjectWithData:[appLinkDataString dataUsingEncoding:NSUTF8StringEncoding]
|
||||
options:0
|
||||
error:&error];
|
||||
if (!error && [applinkData isKindOfClass:[NSDictionary class]]) {
|
||||
// If the version is not specified, assume it is 1.
|
||||
NSString *version = applinkData[BFAppLinkVersionKeyName] ?: @"1.0";
|
||||
NSString *target = applinkData[BFAppLinkTargetKeyName];
|
||||
if ([version isKindOfClass:[NSString class]] &&
|
||||
[version isEqual:BFAppLinkVersion]) {
|
||||
// There's applink data! The target should actually be the applink target.
|
||||
_appLinkData = applinkData;
|
||||
id applinkExtras = applinkData[BFAppLinkExtrasKeyName];
|
||||
if (applinkExtras && [applinkExtras isKindOfClass:[NSDictionary class]]) {
|
||||
_appLinkExtras = applinkExtras;
|
||||
}
|
||||
_targetURL = ([target isKindOfClass:[NSString class]] ? [NSURL URLWithString:target] : url);
|
||||
_targetQueryParameters = [BFURL queryParametersForURL:_targetURL];
|
||||
|
||||
NSDictionary *refererAppLink = _appLinkData[BFAppLinkRefererAppLink];
|
||||
NSString *refererURLString = refererAppLink[BFAppLinkRefererUrl];
|
||||
NSString *refererAppName = refererAppLink[BFAppLinkRefererAppName];
|
||||
|
||||
if (refererURLString && refererAppName) {
|
||||
BFAppLinkTarget *appLinkTarget = [BFAppLinkTarget appLinkTargetWithURL:[NSURL URLWithString:refererURLString]
|
||||
appStoreId:nil
|
||||
appName:refererAppName];
|
||||
_appLinkReferer = [BFAppLink appLinkWithSourceURL:[NSURL URLWithString:refererURLString]
|
||||
targets:@[ appLinkTarget ]
|
||||
webURL:nil
|
||||
isBackToReferrer:YES];
|
||||
}
|
||||
|
||||
// Raise Measurement Event
|
||||
NSString *const EVENT_YES_VAL = @"1";
|
||||
NSString *const EVENT_NO_VAL = @"0";
|
||||
NSMutableDictionary *logData = [[NSMutableDictionary alloc] init];
|
||||
logData[@"version"] = version;
|
||||
if (refererURLString) {
|
||||
logData[@"refererURL"] = refererURLString;
|
||||
}
|
||||
if (refererAppName) {
|
||||
logData[@"refererAppName"] = refererAppName;
|
||||
}
|
||||
if (sourceApplication) {
|
||||
logData[@"sourceApplication"] = sourceApplication;
|
||||
}
|
||||
if ([_targetURL absoluteString]) {
|
||||
logData[@"targetURL"] = [_targetURL absoluteString];
|
||||
}
|
||||
if ([_inputURL absoluteString]) {
|
||||
logData[@"inputURL"] = [_inputURL absoluteString];
|
||||
}
|
||||
if ([_inputURL scheme]) {
|
||||
logData[@"inputURLScheme"] = [_inputURL scheme];
|
||||
}
|
||||
logData[@"forRenderBackToReferrerBar"] = forRenderBackToReferrerBar ? EVENT_YES_VAL : EVENT_NO_VAL;
|
||||
logData[@"forOpenUrl"] = forOpenURLEvent ? EVENT_YES_VAL : EVENT_NO_VAL;
|
||||
[BFMeasurementEvent postNotificationForEventName:BFAppLinkParseEventName args:logData];
|
||||
if (forOpenURLEvent) {
|
||||
[BFMeasurementEvent postNotificationForEventName:BFAppLinkNavigateInEventName args:logData];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
+ (BFURL *)URLWithURL:(NSURL *)url {
|
||||
return [[BFURL alloc] initWithURL:url forOpenInboundURL:NO sourceApplication:nil forRenderBackToReferrerBar:NO];
|
||||
}
|
||||
|
||||
+ (BFURL *)URLWithInboundURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication {
|
||||
return [[BFURL alloc] initWithURL:url forOpenInboundURL:YES sourceApplication:sourceApplication forRenderBackToReferrerBar:NO];
|
||||
}
|
||||
|
||||
+ (BFURL *)URLForRenderBackToReferrerBarURL:(NSURL *)url {
|
||||
return [[BFURL alloc] initWithURL:url forOpenInboundURL:NO sourceApplication:nil forRenderBackToReferrerBar:YES];
|
||||
}
|
||||
|
||||
+ (NSString *)decodeURLString:(NSString *)string {
|
||||
return (NSString *)CFBridgingRelease(CFURLCreateStringByReplacingPercentEscapes(NULL,
|
||||
(CFStringRef)string,
|
||||
CFSTR("")));
|
||||
}
|
||||
|
||||
+ (NSDictionary *)queryParametersForURL:(NSURL *)url {
|
||||
NSMutableDictionary *parameters = [NSMutableDictionary dictionary];
|
||||
NSString *query = url.query;
|
||||
if ([query isEqualToString:@""]) {
|
||||
return @{};
|
||||
}
|
||||
NSArray *queryComponents = [query componentsSeparatedByString:@"&"];
|
||||
for (NSString *component in queryComponents) {
|
||||
NSRange equalsLocation = [component rangeOfString:@"="];
|
||||
if (equalsLocation.location == NSNotFound) {
|
||||
// There's no equals, so associate the key with NSNull
|
||||
parameters[[self decodeURLString:component]] = [NSNull null];
|
||||
} else {
|
||||
NSString *key = [self decodeURLString:[component substringToIndex:equalsLocation.location]];
|
||||
NSString *value = [self decodeURLString:[component substringFromIndex:equalsLocation.location + 1]];
|
||||
parameters[key] = value;
|
||||
}
|
||||
}
|
||||
return [NSDictionary dictionaryWithDictionary:parameters];
|
||||
}
|
||||
|
||||
@end
|
||||
@ -1,26 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 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 <Foundation/Foundation.h>
|
||||
|
||||
#import <Bolts/BFAppLinkResolving.h>
|
||||
|
||||
/*!
|
||||
A reference implementation for an App Link resolver that uses a hidden UIWebView
|
||||
to parse the HTML containing App Link metadata.
|
||||
*/
|
||||
@interface BFWebViewAppLinkResolver : NSObject <BFAppLinkResolving>
|
||||
|
||||
/*!
|
||||
Gets the instance of a BFWebViewAppLinkResolver.
|
||||
*/
|
||||
+ (instancetype)sharedInstance;
|
||||
|
||||
@end
|
||||
296
Example/Pods/Bolts/Bolts/iOS/BFWebViewAppLinkResolver.m
generated
296
Example/Pods/Bolts/Bolts/iOS/BFWebViewAppLinkResolver.m
generated
@ -1,296 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 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 <UIKit/UIKit.h>
|
||||
|
||||
#import "BFWebViewAppLinkResolver.h"
|
||||
#import "BFAppLink.h"
|
||||
#import "BFAppLinkTarget.h"
|
||||
#import "BFTask.h"
|
||||
#import "BFTaskCompletionSource.h"
|
||||
#import "BFExecutor.h"
|
||||
|
||||
// Defines JavaScript to extract app link tags from HTML content
|
||||
static NSString *const BFWebViewAppLinkResolverTagExtractionJavaScript = @""
|
||||
"(function() {"
|
||||
" var metaTags = document.getElementsByTagName('meta');"
|
||||
" var results = [];"
|
||||
" for (var i = 0; i < metaTags.length; i++) {"
|
||||
" var property = metaTags[i].getAttribute('property');"
|
||||
" if (property && property.substring(0, 'al:'.length) === 'al:') {"
|
||||
" var tag = { \"property\": metaTags[i].getAttribute('property') };"
|
||||
" if (metaTags[i].hasAttribute('content')) {"
|
||||
" tag['content'] = metaTags[i].getAttribute('content');"
|
||||
" }"
|
||||
" results.push(tag);"
|
||||
" }"
|
||||
" }"
|
||||
" return JSON.stringify(results);"
|
||||
"})()";
|
||||
static NSString *const BFWebViewAppLinkResolverIOSURLKey = @"url";
|
||||
static NSString *const BFWebViewAppLinkResolverIOSAppStoreIdKey = @"app_store_id";
|
||||
static NSString *const BFWebViewAppLinkResolverIOSAppNameKey = @"app_name";
|
||||
static NSString *const BFWebViewAppLinkResolverDictionaryValueKey = @"_value";
|
||||
static NSString *const BFWebViewAppLinkResolverPreferHeader = @"Prefer-Html-Meta-Tags";
|
||||
static NSString *const BFWebViewAppLinkResolverMetaTagPrefix = @"al";
|
||||
static NSString *const BFWebViewAppLinkResolverWebKey = @"web";
|
||||
static NSString *const BFWebViewAppLinkResolverIOSKey = @"ios";
|
||||
static NSString *const BFWebViewAppLinkResolverIPhoneKey = @"iphone";
|
||||
static NSString *const BFWebViewAppLinkResolverIPadKey = @"ipad";
|
||||
static NSString *const BFWebViewAppLinkResolverWebURLKey = @"url";
|
||||
static NSString *const BFWebViewAppLinkResolverShouldFallbackKey = @"should_fallback";
|
||||
|
||||
@interface BFWebViewAppLinkResolverWebViewDelegate : NSObject <UIWebViewDelegate>
|
||||
|
||||
@property (nonatomic, copy) void (^didFinishLoad)(UIWebView *webView);
|
||||
@property (nonatomic, copy) void (^didFailLoadWithError)(UIWebView *webView, NSError *error);
|
||||
@property (nonatomic, assign) BOOL hasLoaded;
|
||||
|
||||
@end
|
||||
|
||||
@implementation BFWebViewAppLinkResolverWebViewDelegate
|
||||
|
||||
- (void)webViewDidFinishLoad:(UIWebView *)webView {
|
||||
if (self.didFinishLoad) {
|
||||
self.didFinishLoad(webView);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)webViewDidStartLoad:(UIWebView *)webView {
|
||||
}
|
||||
|
||||
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error {
|
||||
if (self.didFailLoadWithError) {
|
||||
self.didFailLoadWithError(webView, error);
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
|
||||
if (self.hasLoaded) {
|
||||
// Consider loading a second resource to be "success", since it indicates an inner frame
|
||||
// or redirect is happening. We can run the tag extraction script at this point.
|
||||
self.didFinishLoad(webView);
|
||||
return NO;
|
||||
}
|
||||
self.hasLoaded = YES;
|
||||
return YES;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation BFWebViewAppLinkResolver
|
||||
|
||||
+ (instancetype)sharedInstance {
|
||||
static id instance;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
instance = [[self alloc] init];
|
||||
});
|
||||
return instance;
|
||||
}
|
||||
|
||||
- (BFTask *)followRedirects:(NSURL *)url {
|
||||
// This task will be resolved with either the redirect NSURL
|
||||
// or a dictionary with the response data to be returned.
|
||||
BFTaskCompletionSource *tcs = [BFTaskCompletionSource taskCompletionSource];
|
||||
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
|
||||
[request setValue:BFWebViewAppLinkResolverMetaTagPrefix forHTTPHeaderField:BFWebViewAppLinkResolverPreferHeader];
|
||||
|
||||
void (^completion)(NSURLResponse *response, NSData *data, NSError *error) = ^(NSURLResponse *response, NSData *data, NSError *error) {
|
||||
if (error) {
|
||||
[tcs setError:error];
|
||||
return;
|
||||
}
|
||||
|
||||
if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
|
||||
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
|
||||
|
||||
// NSURLConnection usually follows redirects automatically, but the
|
||||
// documentation is unclear what the default is. This helps it along.
|
||||
if (httpResponse.statusCode >= 300 && httpResponse.statusCode < 400) {
|
||||
NSString *redirectString = httpResponse.allHeaderFields[@"Location"];
|
||||
NSURL *redirectURL = [NSURL URLWithString:redirectString];
|
||||
[tcs setResult:redirectURL];
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
[tcs setResult:@{ @"response" : response, @"data" : data }];
|
||||
};
|
||||
|
||||
#if __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_7_0 || __MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_9
|
||||
NSURLSession *session = [NSURLSession sharedSession];
|
||||
[[session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
|
||||
completion(response, data, error);
|
||||
}] resume];
|
||||
#else
|
||||
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:completion];
|
||||
#endif
|
||||
|
||||
return [tcs.task continueWithSuccessBlock:^id(BFTask *task) {
|
||||
// If we redirected, just keep recursing.
|
||||
if ([task.result isKindOfClass:[NSURL class]]) {
|
||||
return [self followRedirects:task.result];
|
||||
}
|
||||
return task;
|
||||
}];
|
||||
}
|
||||
|
||||
- (BFTask *)appLinkFromURLInBackground:(NSURL *)url NS_EXTENSION_UNAVAILABLE_IOS("") {
|
||||
return [[self followRedirects:url] continueWithExecutor:[BFExecutor mainThreadExecutor]
|
||||
withSuccessBlock:^id(BFTask *task) {
|
||||
NSData *responseData = task.result[@"data"];
|
||||
NSHTTPURLResponse *response = task.result[@"response"];
|
||||
BFTaskCompletionSource *tcs = [BFTaskCompletionSource taskCompletionSource];
|
||||
|
||||
UIWebView *webView = [[UIWebView alloc] init];
|
||||
BFWebViewAppLinkResolverWebViewDelegate *listener = [[BFWebViewAppLinkResolverWebViewDelegate alloc] init];
|
||||
__block BFWebViewAppLinkResolverWebViewDelegate *retainedListener = listener;
|
||||
listener.didFinishLoad = ^(UIWebView *view) {
|
||||
if (retainedListener) {
|
||||
NSDictionary *ogData = [self getALDataFromLoadedPage:view];
|
||||
[view removeFromSuperview];
|
||||
view.delegate = nil;
|
||||
retainedListener = nil;
|
||||
[tcs setResult:[self appLinkFromALData:ogData destination:url]];
|
||||
}
|
||||
};
|
||||
listener.didFailLoadWithError = ^(UIWebView* view, NSError *error) {
|
||||
if (retainedListener) {
|
||||
[view removeFromSuperview];
|
||||
view.delegate = nil;
|
||||
retainedListener = nil;
|
||||
[tcs setError:error];
|
||||
}
|
||||
};
|
||||
webView.delegate = listener;
|
||||
webView.hidden = YES;
|
||||
[webView loadData:responseData
|
||||
MIMEType:response.MIMEType
|
||||
textEncodingName:response.textEncodingName
|
||||
baseURL:response.URL];
|
||||
UIWindow *window = [UIApplication sharedApplication].windows.firstObject;
|
||||
[window addSubview:webView];
|
||||
|
||||
return tcs.task;
|
||||
}];
|
||||
}
|
||||
|
||||
/*
|
||||
Builds up a data structure filled with the app link data from the meta tags on a page.
|
||||
The structure of this object is a dictionary where each key holds an array of app link
|
||||
data dictionaries. Values are stored in a key called "_value".
|
||||
*/
|
||||
- (NSDictionary *)parseALData:(NSArray *)dataArray {
|
||||
NSMutableDictionary *al = [NSMutableDictionary dictionary];
|
||||
for (NSDictionary *tag in dataArray) {
|
||||
NSString *name = tag[@"property"];
|
||||
if (![name isKindOfClass:[NSString class]]) {
|
||||
continue;
|
||||
}
|
||||
NSArray *nameComponents = [name componentsSeparatedByString:@":"];
|
||||
if (![nameComponents[0] isEqualToString:BFWebViewAppLinkResolverMetaTagPrefix]) {
|
||||
continue;
|
||||
}
|
||||
NSMutableDictionary *root = al;
|
||||
for (NSUInteger i = 1; i < nameComponents.count; i++) {
|
||||
NSMutableArray *children = root[nameComponents[i]];
|
||||
if (!children) {
|
||||
children = [NSMutableArray array];
|
||||
root[nameComponents[i]] = children;
|
||||
}
|
||||
NSMutableDictionary *child = children.lastObject;
|
||||
if (!child || i == nameComponents.count - 1) {
|
||||
child = [NSMutableDictionary dictionary];
|
||||
[children addObject:child];
|
||||
}
|
||||
root = child;
|
||||
}
|
||||
if (tag[@"content"]) {
|
||||
root[BFWebViewAppLinkResolverDictionaryValueKey] = tag[@"content"];
|
||||
}
|
||||
}
|
||||
return al;
|
||||
}
|
||||
|
||||
- (NSDictionary *)getALDataFromLoadedPage:(UIWebView *)webView {
|
||||
// Run some JavaScript in the webview to fetch the meta tags.
|
||||
NSString *jsonString = [webView stringByEvaluatingJavaScriptFromString:BFWebViewAppLinkResolverTagExtractionJavaScript];
|
||||
NSError *error = nil;
|
||||
NSArray *arr = [NSJSONSerialization JSONObjectWithData:[jsonString dataUsingEncoding:NSUTF8StringEncoding]
|
||||
options:0
|
||||
error:&error];
|
||||
return [self parseALData:arr];
|
||||
}
|
||||
|
||||
/*
|
||||
Converts app link data into a BFAppLink containing the targets relevant for this platform.
|
||||
*/
|
||||
- (BFAppLink *)appLinkFromALData:(NSDictionary *)appLinkDict destination:(NSURL *)destination {
|
||||
NSMutableArray *linkTargets = [NSMutableArray array];
|
||||
|
||||
NSArray *platformData = nil;
|
||||
|
||||
const UIUserInterfaceIdiom idiom = UI_USER_INTERFACE_IDIOM();
|
||||
if (idiom == UIUserInterfaceIdiomPad) {
|
||||
platformData = @[ appLinkDict[BFWebViewAppLinkResolverIPadKey] ?: @{},
|
||||
appLinkDict[BFWebViewAppLinkResolverIOSKey] ?: @{} ];
|
||||
} else if (idiom == UIUserInterfaceIdiomPhone) {
|
||||
platformData = @[ appLinkDict[BFWebViewAppLinkResolverIPhoneKey] ?: @{},
|
||||
appLinkDict[BFWebViewAppLinkResolverIOSKey] ?: @{} ];
|
||||
} else {
|
||||
// Future-proofing. Other User Interface idioms should only hit ios.
|
||||
platformData = @[ appLinkDict[BFWebViewAppLinkResolverIOSKey] ?: @{} ];
|
||||
}
|
||||
|
||||
for (NSArray *platformObjects in platformData) {
|
||||
for (NSDictionary *platformDict in platformObjects) {
|
||||
// The schema requires a single url/app store id/app name,
|
||||
// but we could find multiple of them. We'll make a best effort
|
||||
// to interpret this data.
|
||||
NSArray *urls = platformDict[BFWebViewAppLinkResolverIOSURLKey];
|
||||
NSArray *appStoreIds = platformDict[BFWebViewAppLinkResolverIOSAppStoreIdKey];
|
||||
NSArray *appNames = platformDict[BFWebViewAppLinkResolverIOSAppNameKey];
|
||||
|
||||
NSUInteger maxCount = MAX(urls.count, MAX(appStoreIds.count, appNames.count));
|
||||
|
||||
for (NSUInteger i = 0; i < maxCount; i++) {
|
||||
NSString *urlString = urls[i][BFWebViewAppLinkResolverDictionaryValueKey];
|
||||
NSURL *url = urlString ? [NSURL URLWithString:urlString] : nil;
|
||||
NSString *appStoreId = appStoreIds[i][BFWebViewAppLinkResolverDictionaryValueKey];
|
||||
NSString *appName = appNames[i][BFWebViewAppLinkResolverDictionaryValueKey];
|
||||
BFAppLinkTarget *target = [BFAppLinkTarget appLinkTargetWithURL:url
|
||||
appStoreId:appStoreId
|
||||
appName:appName];
|
||||
[linkTargets addObject:target];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NSDictionary *webDict = appLinkDict[BFWebViewAppLinkResolverWebKey][0];
|
||||
NSString *webUrlString = webDict[BFWebViewAppLinkResolverWebURLKey][0][BFWebViewAppLinkResolverDictionaryValueKey];
|
||||
NSString *shouldFallbackString = webDict[BFWebViewAppLinkResolverShouldFallbackKey][0][BFWebViewAppLinkResolverDictionaryValueKey];
|
||||
|
||||
NSURL *webUrl = destination;
|
||||
|
||||
if (shouldFallbackString &&
|
||||
[@[ @"no", @"false", @"0" ] containsObject:[shouldFallbackString lowercaseString]]) {
|
||||
webUrl = nil;
|
||||
}
|
||||
if (webUrl && webUrlString) {
|
||||
webUrl = [NSURL URLWithString:webUrlString];
|
||||
}
|
||||
|
||||
return [BFAppLink appLinkWithSourceURL:destination
|
||||
targets:linkTargets
|
||||
webURL:webUrl];
|
||||
}
|
||||
|
||||
@end
|
||||
@ -1,17 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 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 <Bolts/BFAppLinkReturnToRefererView.h>
|
||||
|
||||
@interface BFAppLinkReturnToRefererView (Internal)
|
||||
|
||||
- (CGFloat)statusBarHeight;
|
||||
|
||||
@end
|
||||
@ -1,32 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 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 <Bolts/BFAppLink.h>
|
||||
|
||||
FOUNDATION_EXPORT NSString *const BFAppLinkDataParameterName;
|
||||
FOUNDATION_EXPORT NSString *const BFAppLinkTargetKeyName;
|
||||
FOUNDATION_EXPORT NSString *const BFAppLinkUserAgentKeyName;
|
||||
FOUNDATION_EXPORT NSString *const BFAppLinkExtrasKeyName;
|
||||
FOUNDATION_EXPORT NSString *const BFAppLinkVersionKeyName;
|
||||
FOUNDATION_EXPORT NSString *const BFAppLinkRefererAppLink;
|
||||
FOUNDATION_EXPORT NSString *const BFAppLinkRefererAppName;
|
||||
FOUNDATION_EXPORT NSString *const BFAppLinkRefererUrl;
|
||||
|
||||
@interface BFAppLink (Internal)
|
||||
|
||||
+ (instancetype)appLinkWithSourceURL:(NSURL *)sourceURL
|
||||
targets:(NSArray *)targets
|
||||
webURL:(NSURL *)webURL
|
||||
isBackToReferrer:(BOOL)isBackToReferrer;
|
||||
|
||||
/*! return if this AppLink is to go back to referrer. */
|
||||
@property (nonatomic, assign, readonly, getter=isBackToReferrer) BOOL backToReferrer;
|
||||
|
||||
@end
|
||||
@ -1,19 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 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 <Bolts/BFMeasurementEvent.h>
|
||||
/*!
|
||||
Provides methods for posting notifications from the Bolts framework
|
||||
*/
|
||||
@interface BFMeasurementEvent (Internal)
|
||||
|
||||
+ (void)postNotificationForEventName:(NSString *)name args:(NSDictionary *)args;
|
||||
|
||||
@end
|
||||
@ -1,15 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 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 <Bolts/BFURL.h>
|
||||
|
||||
@interface BFURL (Internal)
|
||||
+ (BFURL *)URLForRenderBackToReferrerBarURL:(NSURL *)url;
|
||||
@end
|
||||
30
Example/Pods/Bolts/LICENSE
generated
30
Example/Pods/Bolts/LICENSE
generated
@ -1,30 +0,0 @@
|
||||
BSD License
|
||||
|
||||
For Bolts software
|
||||
|
||||
Copyright (c) 2013-present, 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.
|
||||
683
Example/Pods/Bolts/README.md
generated
683
Example/Pods/Bolts/README.md
generated
@ -1,683 +0,0 @@
|
||||
Bolts
|
||||
============
|
||||
[](https://travis-ci.org/BoltsFramework/Bolts-ObjC)
|
||||
[](https://codecov.io/github/BoltsFramework/Bolts-ObjC?branch=master)
|
||||
[](https://cocoapods.org/pods/Bolts)
|
||||
[](https://github.com/BoltsFramework/Bolts-ObjC/blob/master/LICENSE)
|
||||
[](https://www.versioneye.com/objective-c/bolts/references)
|
||||
|
||||
[](https://cocoapods.org/pods/Bolts)
|
||||
[](https://github.com/Carthage/Carthage)
|
||||
|
||||
Bolts is a collection of low-level libraries designed to make developing mobile
|
||||
apps easier. Bolts was designed by Parse and Facebook for our own internal use,
|
||||
and we have decided to open source these libraries to make them available to
|
||||
others. Using these libraries does not require using any Parse services. Nor
|
||||
do they require having a Parse or Facebook developer account.
|
||||
|
||||
Bolts includes:
|
||||
|
||||
* "Tasks", which make organization of complex asynchronous code more manageable. A task is kind of like a JavaScript Promise, but available for iOS and Android.
|
||||
* An implementation of the [App Links protocol](http://applinks.org/), helping you link to content in other apps and handle incoming deep-links.
|
||||
|
||||
For more information, see the [Bolts iOS API Reference](http://boltsframework.github.io/docs/ios/).
|
||||
|
||||
# Tasks
|
||||
|
||||
To build a truly responsive iOS application, you must keep long-running operations off of the UI thread, and be careful to avoid blocking anything the UI thread might be waiting on. This means you will need to execute various operations in the background. To make this easier, we've added a class called `BFTask`. A task represents the result of an asynchronous operation. Typically, a `BFTask` is returned from an asynchronous function and gives the ability to continue processing the result of the task. When a task is returned from a function, it's already begun doing its job. A task is not tied to a particular threading model: it represents the work being done, not where it is executing. Tasks have many advantages over other methods of asynchronous programming, such as callbacks. `BFTask` is not a replacement for `NSOperation` or GCD. In fact, they play well together. But tasks do fill in some gaps that those technologies don't address.
|
||||
* `BFTask` takes care of managing dependencies for you. Unlike using `NSOperation` for dependency management, you don't have to declare all dependencies before starting a `BFTask`. For example, imagine you need to save a set of objects and each one may or may not require saving child objects. With an `NSOperation`, you would normally have to create operations for each of the child saves ahead of time. But you don't always know before you start the work whether that's going to be necessary. That can make managing dependencies with `NSOperation` very painful. Even in the best case, you have to create your dependencies before the operations that depend on them, which results in code that appears in a different order than it executes. With `BFTask`, you can decide during your operation's work whether there will be subtasks and return the other task in just those cases.
|
||||
* `BFTasks` release their dependencies. `NSOperation` strongly retains its dependencies, so if you have a queue of ordered operations and sequence them using dependencies, you have a leak, because every operation gets retained forever. `BFTasks` release their callbacks as soon as they are run, so everything cleans up after itself. This can reduce memory use, and simplify memory management.
|
||||
* `BFTasks` keep track of the state of finished tasks: It tracks whether there was a returned value, the task was cancelled, or if an error occurred. It also has convenience methods for propagating errors. With `NSOperation`, you have to build all of this stuff yourself.
|
||||
* `BFTasks` don't depend on any particular threading model. So it's easy to have some tasks perform their work with an operation queue, while others perform work using blocks with GCD. These tasks can depend on each other seamlessly.
|
||||
* Performing several tasks in a row will not create nested "pyramid" code as you would get when using only callbacks.
|
||||
* `BFTasks` are fully composable, allowing you to perform branching, parallelism, and complex error handling, without the spaghetti code of having many named callbacks.
|
||||
* You can arrange task-based code in the order that it executes, rather than having to split your logic across scattered callback functions.
|
||||
|
||||
For the examples in this doc, assume there are async versions of some common Parse methods, called `saveAsync:` and `findAsync:` which return a `Task`. In a later section, we'll show how to define these functions yourself.
|
||||
|
||||
## The `continueWithBlock` Method
|
||||
|
||||
Every `BFTask` has a method named `continueWithBlock:` which takes a continuation block. A continuation is a block that will be executed when the task is complete. You can then inspect the task to check if it was successful and to get its result.
|
||||
|
||||
```objective-c
|
||||
// Objective-C
|
||||
[[self saveAsync:obj] continueWithBlock:^id(BFTask *task) {
|
||||
if (task.isCancelled) {
|
||||
// the save was cancelled.
|
||||
} else if (task.error) {
|
||||
// the save failed.
|
||||
} else {
|
||||
// the object was saved successfully.
|
||||
PFObject *object = task.result;
|
||||
}
|
||||
return nil;
|
||||
}];
|
||||
```
|
||||
|
||||
```swift
|
||||
// Swift
|
||||
self.saveAsync(obj).continueWithBlock {
|
||||
(task: BFTask!) -> BFTask in
|
||||
if task.isCancelled() {
|
||||
// the save was cancelled.
|
||||
} else if task.error != nil {
|
||||
// the save failed.
|
||||
} else {
|
||||
// the object was saved successfully.
|
||||
var object = task.result() as PFObject
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
BFTasks use Objective-C blocks, so the syntax should be pretty straightforward. Let's look closer at the types involved with an example.
|
||||
|
||||
```objective-c
|
||||
// Objective-C
|
||||
/**
|
||||
* Gets an NSString asynchronously.
|
||||
*/
|
||||
- (BFTask *)getStringAsync {
|
||||
// Let's suppose getNumberAsync returns a BFTask whose result is an NSNumber.
|
||||
return [[self getNumberAsync] continueWithBlock:^id(BFTask *task) {
|
||||
// This continuation block takes the NSNumber BFTask as input,
|
||||
// and provides an NSString as output.
|
||||
|
||||
NSNumber *number = task.result;
|
||||
return [NSString stringWithFormat:@"%@", number];
|
||||
)];
|
||||
}
|
||||
```
|
||||
|
||||
```swift
|
||||
// Swift
|
||||
/**
|
||||
* Gets an NSString asynchronously.
|
||||
*/
|
||||
func getStringAsync() -> BFTask {
|
||||
//Let's suppose getNumberAsync returns a BFTask whose result is an NSNumber.
|
||||
return self.getNumberAsync().continueWithBlock {
|
||||
(task: BFTask!) -> NSString in
|
||||
// This continuation block takes the NSNumber BFTask as input,
|
||||
// and provides an NSString as output.
|
||||
|
||||
let number = task.result() as NSNumber
|
||||
return NSString(format:"%@", number)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
In many cases, you only want to do more work if the previous task was successful, and propagate any errors or cancellations to be dealt with later. To do this, use the `continueWithSuccessBlock:` method instead of `continueWithBlock:`.
|
||||
|
||||
```objective-c
|
||||
// Objective-C
|
||||
[[self saveAsync:obj] continueWithSuccessBlock:^id(BFTask *task) {
|
||||
// the object was saved successfully.
|
||||
return nil;
|
||||
}];
|
||||
```
|
||||
|
||||
```swift
|
||||
// Swift
|
||||
self.saveAsync(obj).continueWithSuccessBlock {
|
||||
(task: BFTask!) -> AnyObject! in
|
||||
// the object was saved successfully.
|
||||
return nil
|
||||
}
|
||||
```
|
||||
|
||||
## Chaining Tasks Together
|
||||
|
||||
BFTasks are a little bit magical, in that they let you chain them without nesting. If you return a BFTask from `continueWithBlock:`, then the task returned by `continueWithBlock:` will not be considered finished until the new task returned from the new continuation block. This lets you perform multiple actions without incurring the pyramid code you would get with callbacks. Likewise, you can return a `BFTask` from `continueWithSuccessBlock:`. So, return a `BFTask` to do more asynchronous work.
|
||||
|
||||
```objective-c
|
||||
// Objective-C
|
||||
PFQuery *query = [PFQuery queryWithClassName:@"Student"];
|
||||
[query orderByDescending:@"gpa"];
|
||||
[[[[[self findAsync:query] continueWithSuccessBlock:^id(BFTask *task) {
|
||||
NSArray *students = task.result;
|
||||
PFObject *valedictorian = [students objectAtIndex:0];
|
||||
[valedictorian setObject:@YES forKey:@"valedictorian"];
|
||||
return [self saveAsync:valedictorian];
|
||||
}] continueWithSuccessBlock:^id(BFTask *task) {
|
||||
PFObject *valedictorian = task.result;
|
||||
return [self findAsync:query];
|
||||
}] continueWithSuccessBlock:^id(BFTask *task) {
|
||||
NSArray *students = task.result;
|
||||
PFObject *salutatorian = [students objectAtIndex:1];
|
||||
[salutatorian setObject:@YES forKey:@"salutatorian"];
|
||||
return [self saveAsync:salutatorian];
|
||||
}] continueWithSuccessBlock:^id(BFTask *task) {
|
||||
// Everything is done!
|
||||
return nil;
|
||||
}];
|
||||
```
|
||||
|
||||
```swift
|
||||
// Swift
|
||||
var query = PFQuery(className:"Student")
|
||||
query.orderByDescending("gpa")
|
||||
findAsync(query).continueWithSuccessBlock {
|
||||
(task: BFTask!) -> BFTask in
|
||||
let students = task.result() as NSArray
|
||||
var valedictorian = students.objectAtIndex(0) as PFObject
|
||||
valedictorian["valedictorian"] = true
|
||||
return self.saveAsync(valedictorian)
|
||||
}.continueWithSuccessBlock {
|
||||
(task: BFTask!) -> BFTask in
|
||||
var valedictorian = task.result() as PFObject
|
||||
return self.findAsync(query)
|
||||
}.continueWithSuccessBlock {
|
||||
(task: BFTask!) -> BFTask in
|
||||
let students = task.result() as NSArray
|
||||
var salutatorian = students.objectAtIndex(1) as PFObject
|
||||
salutatorian["salutatorian"] = true
|
||||
return self.saveAsync(salutatorian)
|
||||
}.continueWithSuccessBlock {
|
||||
(task: BFTask!) -> AnyObject! in
|
||||
// Everything is done!
|
||||
return nil
|
||||
}
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
By carefully choosing whether to call `continueWithBlock:` or `continueWithSuccessBlock:`, you can control how errors are propagated in your application. Using `continueWithBlock:` lets you handle errors by transforming them or dealing with them. You can think of failed tasks kind of like throwing an exception. In fact, if you throw an exception inside a continuation, the resulting task will be faulted with that exception.
|
||||
|
||||
```objective-c
|
||||
// Objective-C
|
||||
PFQuery *query = [PFQuery queryWithClassName:@"Student"];
|
||||
[query orderByDescending:@"gpa"];
|
||||
[[[[[self findAsync:query] continueWithSuccessBlock:^id(BFTask *task) {
|
||||
NSArray *students = task.result;
|
||||
PFObject *valedictorian = [students objectAtIndex:0];
|
||||
[valedictorian setObject:@YES forKey:@"valedictorian"];
|
||||
// Force this callback to fail.
|
||||
return [BFTask taskWithError:[NSError errorWithDomain:@"example.com"
|
||||
code:-1
|
||||
userInfo:nil]];
|
||||
}] continueWithSuccessBlock:^id(BFTask *task) {
|
||||
// Now this continuation will be skipped.
|
||||
PFQuery *valedictorian = task.result;
|
||||
return [self findAsync:query];
|
||||
}] continueWithBlock:^id(BFTask *task) {
|
||||
if (task.error) {
|
||||
// This error handler WILL be called.
|
||||
// The error will be the NSError returned above.
|
||||
// Let's handle the error by returning a new value.
|
||||
// The task will be completed with nil as its value.
|
||||
return nil;
|
||||
}
|
||||
// This will also be skipped.
|
||||
NSArray *students = task.result;
|
||||
PFObject *salutatorian = [students objectAtIndex:1];
|
||||
[salutatorian setObject:@YES forKey:@"salutatorian"];
|
||||
return [self saveAsync:salutatorian];
|
||||
}] continueWithSuccessBlock:^id(BFTask *task) {
|
||||
// Everything is done! This gets called.
|
||||
// The task's result is nil.
|
||||
return nil;
|
||||
}];
|
||||
```
|
||||
|
||||
```swift
|
||||
// Swift
|
||||
var query = PFQuery(className:"Student")
|
||||
query.orderByDescending("gpa")
|
||||
findAsync(query).continueWithSuccessBlock {
|
||||
(task: BFTask!) -> BFTask in
|
||||
let students = task.result() as NSArray
|
||||
var valedictorian = students.objectAtIndex(0) as PFObject
|
||||
valedictorian["valedictorian"] = true
|
||||
//Force this callback to fail.
|
||||
return BFTask(error:NSError(domain:"example.com",
|
||||
code:-1, userInfo: nil))
|
||||
}.continueWithSuccessBlock {
|
||||
(task: BFTask!) -> AnyObject! in
|
||||
//Now this continuation will be skipped.
|
||||
var valedictorian = task.result() as PFObject
|
||||
return self.findAsync(query)
|
||||
}.continueWithBlock {
|
||||
(task: BFTask!) -> AnyObject! in
|
||||
if task.error != nil {
|
||||
// This error handler WILL be called.
|
||||
// The error will be the NSError returned above.
|
||||
// Let's handle the error by returning a new value.
|
||||
// The task will be completed with nil as its value.
|
||||
return nil
|
||||
}
|
||||
// This will also be skipped.
|
||||
let students = task.result() as NSArray
|
||||
var salutatorian = students.objectAtIndex(1) as PFObject
|
||||
salutatorian["salutatorian"] = true
|
||||
return self.saveAsync(salutatorian)
|
||||
}.continueWithSuccessBlock {
|
||||
(task: BFTask!) -> AnyObject! in
|
||||
// Everything is done! This gets called.
|
||||
// The tasks result is nil.
|
||||
return nil
|
||||
}
|
||||
```
|
||||
|
||||
It's often convenient to have a long chain of success callbacks with only one error handler at the end.
|
||||
|
||||
## Creating Tasks
|
||||
|
||||
When you're getting started, you can just use the tasks returned from methods like `findAsync:` or `saveAsync:`. However, for more advanced scenarios, you may want to make your own tasks. To do that, you create a `BFTaskCompletionSource`. This object will let you create a new `BFTask`, and control whether it gets marked as finished or cancelled. After you create a `BFTaskCompletionSource`, you'll need to call `setResult:`, `setError:`, or `cancel` to trigger its continuations.
|
||||
|
||||
```objective-c
|
||||
// Objective-C
|
||||
- (BFTask *)successAsync {
|
||||
BFTaskCompletionSource *successful = [BFTaskCompletionSource taskCompletionSource];
|
||||
[successful setResult:@"The good result."];
|
||||
return successful.task;
|
||||
}
|
||||
|
||||
- (BFTask *)failAsync {
|
||||
BFTaskCompletionSource *failed = [BFTaskCompletionSource taskCompletionSource];
|
||||
[failed setError:[NSError errorWithDomain:@"example.com" code:-1 userInfo:nil]];
|
||||
return failed.task;
|
||||
}
|
||||
```
|
||||
|
||||
```swift
|
||||
// Swift
|
||||
func successAsync() -> BFTask {
|
||||
var successful = BFTaskCompletionSource()
|
||||
successful.setResult("The good result.")
|
||||
return successful.task
|
||||
}
|
||||
|
||||
func failAsync() -> BFTask {
|
||||
var failed = BFTaskCompletionSource()
|
||||
failed.setError(NSError(domain:"example.com", code:-1, userInfo:nil))
|
||||
return failed.task
|
||||
}
|
||||
```
|
||||
|
||||
If you know the result of a task at the time it is created, there are some convenience methods you can use.
|
||||
|
||||
```objective-c
|
||||
// Objective-C
|
||||
BFTask *successful = [BFTask taskWithResult:@"The good result."];
|
||||
|
||||
BFTask *failed = [BFTask taskWithError:anError];
|
||||
```
|
||||
|
||||
```swift
|
||||
// Swift
|
||||
let successful = BFTask(result:"The good result")
|
||||
|
||||
let failed = BFTask(error:anError)
|
||||
```
|
||||
|
||||
## Creating Async Methods
|
||||
|
||||
With these tools, it's easy to make your own asynchronous functions that return tasks. For example, you can make a task-based version of `fetchAsync:` easily.
|
||||
|
||||
```objective-c
|
||||
// Objective-C
|
||||
- (BFTask *) fetchAsync:(PFObject *)object {
|
||||
BFTaskCompletionSource *task = [BFTaskCompletionSource taskCompletionSource];
|
||||
[object fetchInBackgroundWithBlock:^(PFObject *object, NSError *error) {
|
||||
if (!error) {
|
||||
[task setResult:object];
|
||||
} else {
|
||||
[task setError:error];
|
||||
}
|
||||
}];
|
||||
return task.task;
|
||||
}
|
||||
```
|
||||
|
||||
```swift
|
||||
// Swift
|
||||
func fetchAsync(object: PFObject) -> BFTask {
|
||||
var task = BFTaskCompletionSource()
|
||||
object.fetchInBackgroundWithBlock {
|
||||
(object: PFObject?, error: NSError?) -> Void in
|
||||
if error == nil {
|
||||
task.setResult(object)
|
||||
} else {
|
||||
task.setError(error)
|
||||
}
|
||||
}
|
||||
return task.task
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
It's similarly easy to create `saveAsync:`, `findAsync:` or `deleteAsync:`.
|
||||
|
||||
## Tasks in Series
|
||||
|
||||
`BFTasks` are convenient when you want to do a series of tasks in a row, each one waiting for the previous to finish. For example, imagine you want to delete all of the comments on your blog.
|
||||
|
||||
```objective-c
|
||||
// Objective-C
|
||||
PFQuery *query = [PFQuery queryWithClassName:@"Comments"];
|
||||
[query whereKey:@"post" equalTo:@123];
|
||||
|
||||
[[[self findAsync:query] continueWithBlock:^id(BFTask *task) {
|
||||
NSArray *results = task.result;
|
||||
|
||||
// Create a trivial completed task as a base case.
|
||||
BFTask *task = [BFTask taskWithResult:nil];
|
||||
for (PFObject *result in results) {
|
||||
// For each item, extend the task with a function to delete the item.
|
||||
task = [task continueWithBlock:^id(BFTask *task) {
|
||||
// Return a task that will be marked as completed when the delete is finished.
|
||||
return [self deleteAsync:result];
|
||||
}];
|
||||
}
|
||||
return task;
|
||||
}] continueWithBlock:^id(BFTask *task) {
|
||||
// Every comment was deleted.
|
||||
return nil;
|
||||
}];
|
||||
```
|
||||
|
||||
```swift
|
||||
// Swift
|
||||
var query = PFQuery(className:"Comments")
|
||||
query.whereKey("post", equalTo:123)
|
||||
findAsync(query).continueWithBlock {
|
||||
(task: BFTask!) -> BFTask in
|
||||
let results = task.result() as NSArray
|
||||
|
||||
// Create a trivial completed task as a base case.
|
||||
let task = BFTask(result:nil)
|
||||
for result : PFObject in results {
|
||||
// For each item, extend the task with a function to delete the item.
|
||||
task = task.continueWithBlock {
|
||||
(task: BFTask!) -> BFTask in
|
||||
return self.deleteAsync(result)
|
||||
}
|
||||
}
|
||||
return task
|
||||
}.continueWithBlock {
|
||||
(task: BFTask!) -> AnyObject! in
|
||||
// Every comment was deleted.
|
||||
return nil
|
||||
}
|
||||
```
|
||||
|
||||
## Tasks in Parallel
|
||||
|
||||
You can also perform several tasks in parallel, using the `taskForCompletionOfAllTasks:` method. You can start multiple operations at once, and use `taskForCompletionOfAllTasks:` to create a new task that will be marked as completed when all of its input tasks are completed. The new task will be successful only if all of the passed-in tasks succeed. Performing operations in parallel will be faster than doing them serially, but may consume more system resources and bandwidth.
|
||||
|
||||
```objective-c
|
||||
// Objective-C
|
||||
PFQuery *query = [PFQuery queryWithClassName:@"Comments"];
|
||||
[query whereKey:@"post" equalTo:@123];
|
||||
|
||||
[[[self findAsync:query] continueWithBlock:^id(BFTask *results) {
|
||||
// Collect one task for each delete into an array.
|
||||
NSMutableArray *tasks = [NSMutableArray array];
|
||||
for (PFObject *result in results) {
|
||||
// Start this delete immediately and add its task to the list.
|
||||
[tasks addObject:[self deleteAsync:result]];
|
||||
}
|
||||
// Return a new task that will be marked as completed when all of the deletes are
|
||||
// finished.
|
||||
return [BFTask taskForCompletionOfAllTasks:tasks];
|
||||
}] continueWithBlock:^id(BFTask *task) {
|
||||
// Every comment was deleted.
|
||||
return nil;
|
||||
}];
|
||||
```
|
||||
|
||||
```swift
|
||||
// Swift
|
||||
var query = PFQuery(className:"Comments")
|
||||
query.whereKey("post", equalTo:123)
|
||||
|
||||
findAsync(query).continueWithBlock {
|
||||
(task: BFTask!) -> BFTask in
|
||||
// Collect one task for each delete into an array.
|
||||
var tasks = NSMutableArray.array()
|
||||
var results = task.result() as NSArray
|
||||
for result : PFObject! in results {
|
||||
// Start this delete immediately and add its task to the list.
|
||||
tasks.addObject(self.deleteAsync(result))
|
||||
}
|
||||
// Return a new task that will be marked as completed when all of the deletes
|
||||
// are finished.
|
||||
return BFTask(forCompletionOfAllTasks:tasks)
|
||||
}.continueWithBlock {
|
||||
(task: BFTask!) -> AnyObject! in
|
||||
// Every comment was deleted.
|
||||
return nil
|
||||
}
|
||||
```
|
||||
|
||||
## Task Executors
|
||||
|
||||
Both `continueWithBlock:` and `continueWithSuccessBlock:` methods have another form that takes an instance of `BFExecutor`. These are `continueWithExecutor:withBlock:` and `continueWithExecutor:withSuccessBlock:`. These methods allow you to control how the continuation is executed. The default executor will dispatch to GCD, but you can provide your own executor to schedule work onto a different thread. For example, if you want to continue with work on the UI thread:
|
||||
|
||||
```objective-c
|
||||
// Create a BFExecutor that uses the main thread.
|
||||
BFExecutor *myExecutor = [BFExecutor executorWithBlock:^void(void(^block)()) {
|
||||
dispatch_async(dispatch_get_main_queue(), block);
|
||||
}];
|
||||
|
||||
// And use the Main Thread Executor like this. The executor applies only to the new
|
||||
// continuation being passed into continueWithBlock.
|
||||
[[self fetchAsync:object] continueWithExecutor:myExecutor withBlock:^id(BFTask *task) {
|
||||
myTextView.text = [object objectForKey:@"name"];
|
||||
}];
|
||||
```
|
||||
|
||||
For common cases, such as dispatching on the main thread, we have provided default implementations of `BFExecutor`. These include `defaultExecutor`, `immediateExecutor`, `mainThreadExecutor`, `executorWithDispatchQueue:`, and `executorWithOperationQueue:`. For example:
|
||||
|
||||
```objective-c
|
||||
// Continue on the Main Thread, using a built-in executor.
|
||||
[[self fetchAsync:object] continueWithExecutor:[BFExecutor mainThreadExecutor] withBlock:^id(BFTask *task) {
|
||||
myTextView.text = [object objectForKey:@"name"];
|
||||
}];
|
||||
```
|
||||
|
||||
## Task Cancellation
|
||||
|
||||
It's generally bad design to keep track of the `BFTaskCompletionSource` for cancellation. A better model is to create a "cancellation token" at the top level, and pass that to each async function that you want to be part of the same "cancelable operation". Then, in your continuation blocks, you can check whether the cancellation token has been cancelled and bail out early by returning a `[BFTask cancelledTask]`. For example:
|
||||
|
||||
```objective-c
|
||||
- (void)doSomethingComplicatedAsync:(MYCancellationToken *)cancellationToken {
|
||||
[[self doSomethingAsync:cancellationToken] continueWithBlock:^{
|
||||
if (cancellationToken.isCancelled) {
|
||||
return [BFTask cancelledTask];
|
||||
}
|
||||
// Do something that takes a while.
|
||||
return result;
|
||||
}];
|
||||
}
|
||||
|
||||
// Somewhere else.
|
||||
MYCancellationToken *cancellationToken = [[MYCancellationToken alloc] init];
|
||||
[obj doSomethingComplicatedAsync:cancellationToken];
|
||||
|
||||
// When you get bored...
|
||||
[cancellationToken cancel];
|
||||
```
|
||||
|
||||
**Note:** The cancellation token implementation should be thread-safe.
|
||||
We are likely to add some concept like this to Bolts at some point in the future.
|
||||
|
||||
# App Links
|
||||
|
||||
[App Links](http://applinks.org/) provide a cross-platform mechanism that allows a developer to define and publish a deep-linking scheme for their content, allowing other apps to link directly to an experience optimized for the device they are running on. Whether you are building an app that receives incoming links or one that may link out to other apps' content, Bolts provides tools to simplify implementation of the [App Links protocol](http://applinks.org/documentation).
|
||||
|
||||
## Handling an App Link
|
||||
|
||||
The most common case will be making your app receive App Links. In-linking will allow your users to quickly access the richest, most native-feeling presentation of linked content on their devices. Bolts makes it easy to handle an inbound App Link (as well as general inbound deep-links) by providing utilities for processing an incoming URL.
|
||||
|
||||
For example, you can use the `BFURL` utility class to parse an incoming URL in your `AppDelegate`:
|
||||
|
||||
```objective-c
|
||||
- (BOOL)application:(UIApplication *)application
|
||||
openURL:(NSURL *)url
|
||||
sourceApplication:(NSString *)sourceApplication
|
||||
annotation:(id)annotation {
|
||||
BFURL *parsedUrl = [BFURL URLWithInboundURL:url sourceApplication:sourceApplication];
|
||||
|
||||
// Use the target URL from the App Link to locate content.
|
||||
if ([parsedUrl.targetURL.pathComponents[1] isEqualToString:@"profiles"]) {
|
||||
// Open a profile viewer.
|
||||
}
|
||||
|
||||
// You can also check the query string easily.
|
||||
NSString *query = parsedUrl.targetQueryParameters[@"query"];
|
||||
|
||||
// Apps that have existing deep-linking support and map their App Links to existing
|
||||
// deep-linking functionality may instead want to perform these operations on the input URL.
|
||||
// Use the target URL from the App Link to locate content.
|
||||
if ([parsedUrl.inputURL.pathComponents[1] isEqualToString:@"profiles"]) {
|
||||
// Open a profile viewer.
|
||||
}
|
||||
|
||||
// You can also check the query string easily.
|
||||
NSString *query = parsedUrl.inputQueryParameters[@"query"];
|
||||
|
||||
// Apps can easily check the Extras and App Link data from the App Link as well.
|
||||
NSString *fbAccessToken = parsedUrl.appLinkExtras[@"fb_access_token"];
|
||||
NSDictionary *refererData = parsedUrl.appLinkExtras[@"referer"];
|
||||
}
|
||||
```
|
||||
|
||||
## Navigating to a URL
|
||||
|
||||
Following an App Link allows your app to provide the best user experience (as defined by the receiving app) when a user navigates to a link. Bolts makes this process simple, automating the steps required to follow a link:
|
||||
|
||||
1. Resolve the App Link by getting the App Link metadata from the HTML at the URL specified.
|
||||
2. Step through App Link targets relevant to the device being used, checking whether the app that can handle the target is present on the device.
|
||||
3. If an app is present, build a URL with the appropriate al_applink_data specified and navigate to that URL.
|
||||
4. Otherwise, open the browser with the original URL specified.
|
||||
|
||||
In the simplest case, it takes just one line of code to navigate to a URL that may have an App Link:
|
||||
|
||||
```objective-c
|
||||
[BFAppLinkNavigation navigateToURLInBackground:url];
|
||||
```
|
||||
|
||||
### Adding App and Navigation Data
|
||||
|
||||
Under most circumstances, the data that will need to be passed along to an app during a navigation will be contained in the URL itself, so that whether or not the app is actually installed on the device, users are taken to the correct content. Occasionally, however, apps will want to pass along data that is relevant for app-to-app navigation, or will want to augment the App Link protocol with information that might be used by the app to adjust how the app should behave (e.g. showing a link back to the referring app).
|
||||
|
||||
If you want to take advantage of these features, you can break apart the navigation process. First, you must have an App Link to which you wish to navigate:
|
||||
|
||||
```objective-c
|
||||
[[BFAppLinkNavigation resolveAppLinkInBackground:url] continueWithSuccessBlock:^id(BFTask *task) {
|
||||
BFAppLink *link = task.result;
|
||||
}];
|
||||
```
|
||||
|
||||
Then, you can build an App Link request with any additional data you would like and navigate:
|
||||
|
||||
```objective-c
|
||||
BFAppLinkNavigation *navigation = [BFAppLinkNavigation navigationWithAppLink:link
|
||||
extras:@{ @"access_token": @"t0kEn" }
|
||||
appLinkData:@{ @"ref": @"12345" }];
|
||||
NSError *error = nil;
|
||||
[navigation navigate:&error];
|
||||
```
|
||||
|
||||
### Resolving App Link Metadata
|
||||
|
||||
Bolts allows for custom App Link resolution, which may be used as a performance optimization (e.g. caching the metadata) or as a mechanism to allow developers to use a centralized index for obtaining App Link metadata. A custom App Link resolver just needs to be able to take a URL and return a `BFAppLink` containing the ordered list of `BFAppLinkTarget`s that are applicable for this device. Bolts provides one of these out of the box that performs this resolution on the device using a hidden UIWebView.
|
||||
|
||||
You can use any resolver that implements the `BFAppLinkResolving` protocol by using one of the overloads on `BFAppLinkNavigation`:
|
||||
|
||||
```objective-c
|
||||
[BFAppLinkNavigation navigateToURLInBackground:url
|
||||
resolver:resolver];
|
||||
```
|
||||
|
||||
Alternatively, a you can swap out the default resolver to be used by the built-in APIs:
|
||||
|
||||
```objective-c
|
||||
[BFAppLinkNavigation setDefaultResolver:resolver];
|
||||
[BFAppLinkNavigation navigateToURLInBackground:url];
|
||||
```
|
||||
|
||||
## App Link Return-to-Referer View
|
||||
|
||||
When an application is opened via an App Link, a banner allowing the user to "Touch to return to <calling app>" should be displayed. The `BFAppLinkReturnToRefererView` provides this functionality. It will take an incoming App Link and parse the referer information to display the appropriate calling app name.
|
||||
|
||||
```objective-c
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
|
||||
// Perform other view initialization.
|
||||
|
||||
self.returnToRefererController = [[BFAppLinkReturnToRefererController alloc] init];
|
||||
|
||||
// self.returnToRefererView is a BFAppLinkReturnToRefererView.
|
||||
// You may initialize the view either by loading it from a NIB or programmatically.
|
||||
self.returnToRefererController.view = self.returnToRefererView;
|
||||
|
||||
// If you have a UINavigationController in the view, then the bar must be shown above it.
|
||||
[self.returnToRefererController]
|
||||
}
|
||||
```
|
||||
|
||||
The following code assumes that the view controller has an `openedAppLinkURL` `NSURL` property that has already been populated with the URL used to open the app. You can then do something like this to show the view:
|
||||
|
||||
```objective-c
|
||||
- (void)viewWillAppear {
|
||||
[super viewWillAppear];
|
||||
|
||||
// Show only if you have a back AppLink.
|
||||
[self.returnToRefererController showViewForRefererURL:self.openedAppLinkURL];
|
||||
}
|
||||
```
|
||||
|
||||
In a navigation-controller view hierarchy, the banner should be displayed above the navigation bar, and `BFAppLinkReturnToRefererController` provides an `initForDisplayAboveNavController` method to assist with this.
|
||||
|
||||
## Analytics
|
||||
|
||||
Bolts introduces Measurement Event. App Links posts three different Measurement Event notifications to the application, which can be caught and integrated with existing analytics components in your application.
|
||||
|
||||
* `al_nav_out` — Raised when your app switches out to an App Links URL.
|
||||
* `al_nav_in` — Raised when your app opens an incoming App Links URL.
|
||||
* `al_ref_back_out` — Raised when your app returns back the referrer app using the built-in top navigation back bar view.
|
||||
|
||||
### Listen for App Links Measurement Events
|
||||
|
||||
There are other analytics tools that are integrated with Bolts' App Links events, but you can also listen for these events yourself:
|
||||
|
||||
```objective-c
|
||||
[[NSNotificationCenter defaultCenter] addObserverForName:BFMeasurementEventNotificationName object:nil queue:nil usingBlock:^(NSNotification *note) {
|
||||
NSDictionary *event = note.userInfo;
|
||||
NSDictionary *eventData = event[BFMeasurementEventArgsKey];
|
||||
// Integrate to your logging/analytics component.
|
||||
}];
|
||||
```
|
||||
|
||||
### App Links Event Fields
|
||||
|
||||
App Links Measurement Events sends additional information from App Links Intents in flattened string key value pairs. Here are some of the useful fields for the three events.
|
||||
|
||||
* `al_nav_in`
|
||||
* `inputURL`: the URL that opens the app.
|
||||
* `inputURLScheme`: the scheme of `inputURL`.
|
||||
* `refererURL`: the URL that the referrer app added into `al_applink_data`: `referer_app_link`.
|
||||
* `refererAppName`: the app name that the referrer app added to `al_applink_data`: `referer_app_link`.
|
||||
* `sourceApplication`: the bundle of referrer application.
|
||||
* `targetURL`: the `target_url` field in `al_applink_data`.
|
||||
* `version`: App Links API version.
|
||||
|
||||
* `al_nav_out` / `al_ref_back_out`
|
||||
* `outputURL`: the URL used to open the other app (or browser). If there is an eligible app to open, this will be the custom scheme url/intent in `al_applink_data`.
|
||||
* `outputURLScheme`: the scheme of `outputURL`.
|
||||
* `sourceURL`: the URL of the page hosting App Links meta tags.
|
||||
* `sourceURLHost`: the hostname of `sourceURL`.
|
||||
* `success`: `“1”` to indicate success in opening the App Link in another app or browser; `“0”` to indicate failure to open the App Link.
|
||||
* `type`: `“app”` for open in app, `“web”` for open in browser; `“fail”` when the success field is `“0”`.
|
||||
* `version`: App Links API version.
|
||||
|
||||
# Installation
|
||||
|
||||
You can download the latest framework files from our [Releases page](https://github.com/BoltsFramework/Bolts-ObjC/releases).
|
||||
|
||||
Bolts is also available through [CocoaPods](https://cocoapods.org/). To install it simply add the following line to your Podfile:
|
||||
|
||||
pod 'Bolts'
|
||||
@ -1,13 +0,0 @@
|
||||
//
|
||||
// DJLocalizableString.h
|
||||
// DJLocalization
|
||||
//
|
||||
// Created by David Jennes on 15/02/15.
|
||||
// Copyright (c) 2015. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface DJLocalizableString : NSString
|
||||
|
||||
@end
|
||||
@ -1,59 +0,0 @@
|
||||
//
|
||||
// DJLocalizableString.m
|
||||
// DJLocalization
|
||||
//
|
||||
// Created by David Jennes on 15/02/15.
|
||||
// Copyright (c) 2015. All rights reserved.
|
||||
//
|
||||
|
||||
#import "DJLocalizableString.h"
|
||||
|
||||
#import "DJLocalizationSystem+Private.h"
|
||||
|
||||
static NSString * const kDJLocalizableString = @"NSLocalizableString";
|
||||
|
||||
@interface DJLocalizableString ()
|
||||
|
||||
@property (nonatomic, strong) NSString *developmentLanguageString;
|
||||
@property (nonatomic, strong) NSString *stringsFileKey;
|
||||
|
||||
@end
|
||||
|
||||
@implementation DJLocalizableString
|
||||
|
||||
+ (void)load {
|
||||
@autoreleasepool {
|
||||
[NSKeyedUnarchiver setClass: DJLocalizableString.class forClassName: kDJLocalizableString];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - NSCoding
|
||||
|
||||
- (id)initWithCoder:(NSCoder *)aDecoder {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
self.stringsFileKey = [aDecoder decodeObjectForKey: @"NSKey"];
|
||||
self.developmentLanguageString = [aDecoder decodeObjectForKey: @"NSDev"];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark - NSObject
|
||||
|
||||
- (id)awakeAfterUsingCoder:(NSCoder *)aDecoder {
|
||||
return [DJLocalizationSystem.shared localizedStoryboardStringForKey: self.stringsFileKey
|
||||
value: self.developmentLanguageString];
|
||||
}
|
||||
|
||||
#pragma mark - NSString
|
||||
|
||||
- (NSUInteger)length {
|
||||
return self.developmentLanguageString.length;
|
||||
}
|
||||
|
||||
- (unichar)characterAtIndex:(NSUInteger)index {
|
||||
return [self.developmentLanguageString characterAtIndex: index];
|
||||
}
|
||||
|
||||
@end
|
||||
@ -1,15 +0,0 @@
|
||||
//
|
||||
// DJLocalization.h
|
||||
// DJLocalization
|
||||
//
|
||||
// Created by David Jennes on 04/03/15.
|
||||
// Copyright (c) 2015. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef DJLocalization_h
|
||||
#define DJLocalization_h
|
||||
|
||||
#import "DJLocalizationSystem.h"
|
||||
#import "UIStoryboard+DJLocalization.h"
|
||||
|
||||
#endif
|
||||
@ -1,23 +0,0 @@
|
||||
//
|
||||
// DJLocalizationSystem+Private.h
|
||||
// DJLocalization
|
||||
//
|
||||
// Created by David Jennes on 16/02/16.
|
||||
// Copyright (c) 2015. All rights reserved.
|
||||
//
|
||||
|
||||
#import "DJLocalizationSystem.h"
|
||||
|
||||
@interface DJLocalizationSystem (Private)
|
||||
|
||||
/*!
|
||||
* Special version that tries to load a storyboard string from all the tables
|
||||
* except Localizable and InfoPlist. Defaults to comment if not found.
|
||||
*
|
||||
* @param key The localization key
|
||||
* @param comment The localization value
|
||||
* @return The localized string
|
||||
*/
|
||||
- (NSString *)localizedStoryboardStringForKey:(NSString *)key value:(NSString *)comment;
|
||||
|
||||
@end
|
||||
@ -1,69 +0,0 @@
|
||||
//
|
||||
// DJLocalizationSystem.h
|
||||
// DJLocalization
|
||||
//
|
||||
// Created by David Jennes on 15/02/15.
|
||||
// Copyright (c) 2015. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#define DJLocalizedString(key, comment) \
|
||||
[DJLocalizationSystem.shared localizedStringForKey:(key) value: @"" table: nil]
|
||||
#define DJLocalizedStringFromTable(key, tbl, comment) \
|
||||
[DJLocalizationSystem.shared localizedStringForKey:(key) value: @"" table:(tbl)]
|
||||
|
||||
// replace Apple's macros with our own
|
||||
#undef NSLocalizedString
|
||||
#define NSLocalizedString(key, comment) \
|
||||
DJLocalizedString(key, comment)
|
||||
|
||||
#undef NSLocalizedStringFromTable
|
||||
#define NSLocalizedStringFromTable(key, tbl, comment) \
|
||||
DJLocalizedStringFromTable(key, tbl, comment)
|
||||
|
||||
@interface DJLocalizationSystem : NSObject
|
||||
|
||||
/*!
|
||||
* Get the system's global instance
|
||||
*
|
||||
* @return A localization system instance
|
||||
*/
|
||||
@property (class, nonnull, readonly) DJLocalizationSystem *shared;
|
||||
|
||||
/*!
|
||||
* Get the localized string for a given key/value
|
||||
*
|
||||
* @param key The localization key
|
||||
* @param comment The localization value
|
||||
* @param tableName The localization table name
|
||||
* @return The localized string
|
||||
*/
|
||||
- (nonnull NSString *)localizedStringForKey:(nullable NSString *)key value:(nullable NSString *)comment table:(nullable NSString *)tableName;
|
||||
|
||||
/*!
|
||||
* Get the localized string for a given key/value in the specified bundle
|
||||
*
|
||||
* @param key The localization key
|
||||
* @param comment The localization value
|
||||
* @param bundle The bundle to search in
|
||||
* @return The localized string
|
||||
*/
|
||||
- (nonnull NSString *)localizedStringForKey:(nullable NSString *)key value:(nullable NSString *)comment table:(nullable NSString *)tableName bundle:(nonnull NSBundle *)bundle;
|
||||
|
||||
/*!
|
||||
* Reset the localization to the system's default language
|
||||
*/
|
||||
- (void)resetLocalization;
|
||||
|
||||
/*!
|
||||
* The bundle for the currently active language
|
||||
*/
|
||||
@property (nonatomic, readonly, nonnull) NSBundle *bundle;
|
||||
|
||||
/*!
|
||||
* The currently active language
|
||||
*/
|
||||
@property (nonatomic, strong, nonnull) NSString *language;
|
||||
|
||||
@end
|
||||
@ -1,136 +0,0 @@
|
||||
//
|
||||
// DJLocalizationSystem.m
|
||||
// DJLocalization
|
||||
//
|
||||
// Created by David Jennes on 15/02/15.
|
||||
// Copyright (c) 2015. All rights reserved.
|
||||
//
|
||||
|
||||
#import "DJLocalization.h"
|
||||
#import "DJLocalizationSystem+Private.h"
|
||||
|
||||
@interface DJLocalizationSystem ()
|
||||
|
||||
@property (nonatomic, strong) NSBundle *bundle;
|
||||
@property (nonatomic, strong) NSDictionary *storyboardStrings;
|
||||
@property (nonatomic, strong) NSString *customLanguage;
|
||||
|
||||
@end
|
||||
|
||||
@implementation DJLocalizationSystem
|
||||
|
||||
+ (instancetype)shared {
|
||||
__strong static DJLocalizationSystem *sharedSystem = nil;
|
||||
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
sharedSystem = [self new];
|
||||
});
|
||||
|
||||
return sharedSystem;
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
if (!self)
|
||||
return nil;
|
||||
|
||||
[self resetLocalization];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSString *)localizedStoryboardStringForKey:(NSString *)key value:(NSString *)comment {
|
||||
NSString *result = self.storyboardStrings[key];
|
||||
if (!result)
|
||||
result = comment;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
- (NSString *)localizedStringForKey:(NSString *)key value:(NSString *)value table:(NSString *)tableName {
|
||||
return [self.bundle localizedStringForKey: key value: value table: tableName];
|
||||
}
|
||||
|
||||
- (NSString *)localizedStringForKey:(NSString *)key value:(NSString *)value table:(NSString *)tableName bundle:(NSBundle *)bundle {
|
||||
// if no custom language, just get the standard localized string
|
||||
if (!self.customLanguage)
|
||||
return [bundle localizedStringForKey: key value: value table: tableName];
|
||||
|
||||
// check if our custom language exists
|
||||
NSString *path = [bundle pathForResource: self.customLanguage ofType: @"lproj"];
|
||||
if (path)
|
||||
return [[NSBundle bundleWithPath: path] localizedStringForKey: key value: value table: tableName];
|
||||
|
||||
// otherwise return the default
|
||||
return [bundle localizedStringForKey: key value: value table: tableName];
|
||||
}
|
||||
|
||||
- (void)resetLocalization {
|
||||
self.language = NSBundle.mainBundle.preferredLocalizations.firstObject;
|
||||
}
|
||||
|
||||
- (void)setLanguage:(NSString*)language {
|
||||
if ([language isEqualToString: self.customLanguage ])
|
||||
return;
|
||||
NSDictionary *languageInfo = [NSLocale componentsFromLocaleIdentifier: language];
|
||||
|
||||
// find bundle
|
||||
NSString *path = [NSBundle.mainBundle pathForResource: language ofType: @"lproj"];
|
||||
if (path == nil)
|
||||
path = [NSBundle.mainBundle pathForResource: languageInfo[NSLocaleLanguageCode] ofType: @"lproj"];
|
||||
|
||||
// set bundle
|
||||
if (path) {
|
||||
self.bundle = [NSBundle bundleWithPath: path];
|
||||
[self loadTables];
|
||||
|
||||
// store language id
|
||||
self.customLanguage = language;
|
||||
} else {
|
||||
NSLog(@"Localization error: no bundle found, resetting!");
|
||||
self.bundle = NSBundle.mainBundle;
|
||||
self.customLanguage = nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (NSString*)language {
|
||||
if (self.customLanguage)
|
||||
return self.customLanguage;
|
||||
else {
|
||||
NSString *language = NSLocale.preferredLanguages.firstObject;
|
||||
NSDictionary *languageInfo = [NSLocale componentsFromLocaleIdentifier: language];
|
||||
|
||||
return languageInfo[NSLocaleLanguageCode];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Helper methods
|
||||
|
||||
- (void)loadTables {
|
||||
NSMutableDictionary *strings = [NSMutableDictionary new];
|
||||
|
||||
// find strings files
|
||||
NSFileManager *fileManager = NSFileManager.defaultManager;
|
||||
NSURL *bundleURL = self.bundle.bundleURL;
|
||||
NSArray *contents = [fileManager contentsOfDirectoryAtURL: bundleURL
|
||||
includingPropertiesForKeys: @[]
|
||||
options: NSDirectoryEnumerationSkipsHiddenFiles
|
||||
error: nil];
|
||||
|
||||
// load them
|
||||
NSPredicate *predicate = [NSPredicate predicateWithFormat: @"pathExtension == 'strings'"];
|
||||
for (NSURL *fileURL in [contents filteredArrayUsingPredicate: predicate]) {
|
||||
if ([fileURL.lastPathComponent isEqualToString: @"Localizable.strings"])
|
||||
continue;
|
||||
if ([fileURL.lastPathComponent isEqualToString: @"InfoPlist.strings"])
|
||||
continue;
|
||||
|
||||
NSDictionary *table = [NSDictionary dictionaryWithContentsOfURL: fileURL];
|
||||
[strings addEntriesFromDictionary: table];
|
||||
}
|
||||
|
||||
self.storyboardStrings = strings;
|
||||
}
|
||||
|
||||
@end
|
||||
@ -1,21 +0,0 @@
|
||||
//
|
||||
// UIStoryboard+DJLocalization.h
|
||||
// DJLocalization
|
||||
//
|
||||
// Created by David Jennes on 15/02/15.
|
||||
// Copyright (c) 2015. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface UIStoryboard (DJLocalization)
|
||||
|
||||
/*!
|
||||
* Get the storyboard for the active language
|
||||
*
|
||||
* @param name The storyboard's name
|
||||
* @return The desired storyboard
|
||||
*/
|
||||
+ (nonnull instancetype)dj_storyboardWithName:(nonnull NSString *)name;
|
||||
|
||||
@end
|
||||
@ -1,19 +0,0 @@
|
||||
//
|
||||
// UIStoryboard+DJLocalization.m
|
||||
// DJLocalization
|
||||
//
|
||||
// Created by David Jennes on 15/02/15.
|
||||
// Copyright (c) 2015. All rights reserved.
|
||||
//
|
||||
|
||||
#import "UIStoryboard+DJLocalization.h"
|
||||
|
||||
#import "DJLocalizationSystem.h"
|
||||
|
||||
@implementation UIStoryboard (DJLocalization)
|
||||
|
||||
+ (instancetype)dj_storyboardWithName:(NSString *)name {
|
||||
return [self storyboardWithName: name bundle: DJLocalizationSystem.shared.bundle];
|
||||
}
|
||||
|
||||
@end
|
||||
19
Example/Pods/DJLocalization/LICENSE
generated
19
Example/Pods/DJLocalization/LICENSE
generated
@ -1,19 +0,0 @@
|
||||
Copyright (c) 2015 David Jennes <david.jennes@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.
|
||||
75
Example/Pods/DJLocalization/README.md
generated
75
Example/Pods/DJLocalization/README.md
generated
@ -1,75 +0,0 @@
|
||||
# DJLocalization
|
||||
|
||||
[](http://cocoadocs.org/docsets/DJLocalization)
|
||||
[](http://cocoadocs.org/docsets/DJLocalization)
|
||||
[](http://cocoadocs.org/docsets/DJLocalization)
|
||||
|
||||
Localization system that allows language switching at runtime. Supports both code strings (NSLocalizedString) and storyboards (base internationalization).
|
||||
|
||||
## Demo
|
||||
|
||||
To try the example project, just run the following command:
|
||||
|
||||
pod try DJLocalization
|
||||
|
||||
## Requirements
|
||||
|
||||
Requires iOS 6 or higher.
|
||||
|
||||
## Installation
|
||||
|
||||
### From CocoaPods
|
||||
|
||||
DJLocalization is available through [CocoaPods](http://cocoapods.org). To install
|
||||
it, simply add the following line to your Podfile:
|
||||
|
||||
pod "DJLocalization"
|
||||
|
||||
For Swift, use:
|
||||
|
||||
pod "DJLocalization/Swift"
|
||||
|
||||
|
||||
### Manually
|
||||
|
||||
_**Important note if your project doesn't use ARC**: you must add the `-fobjc-arc` compiler flag to all 'DJLocalization' files in Target Settings > Build Phases > Compile Sources._
|
||||
|
||||
* Drag the `DJLocalization/DJlocalization` folder into your project.
|
||||
|
||||
## Usage
|
||||
|
||||
### Importing headers
|
||||
|
||||
Then simply add the following import to your prefix header, or any file where you use the NSLocalizedString macro (or variants).
|
||||
|
||||
@import DJLocalization;
|
||||
|
||||
#####*Important:*
|
||||
You must import the header wherever you use the NSLocalizedString macro, as this header replaces the default NSLocalizedString macro with a custom one. If you don't, runtime switching of languages won't work.
|
||||
|
||||
### Localization
|
||||
|
||||
Use your normal localization workflow and everything should work correctly. For code strings use `NSLocalizedString`, for storyboards use Base Internationalization.
|
||||
|
||||
As mentioned before, `NSLocalizedString` will only work correctly if you first include the correct headers. If you want a bit more safety, the library provides some extra macros `DJLocalizedString` that provide the same functionality as the overridden `NSLocalizedString` macros. This way you'll get a compilation error if you forget to include the headers.
|
||||
|
||||
### Switching languages
|
||||
|
||||
At runtime, you can switch the language at any time by setting the language property:
|
||||
|
||||
[DJLocalizationSystem shared].language = @"en";
|
||||
|
||||
Be careful to only use language codes (en, nl, fr, etc...) for localizations that exist in your project.
|
||||
|
||||
#####*Important:*
|
||||
Views are translated on load. This means that if you switch the language at some point, only subsequently loaded views will have the correct language strings. Ideally, when you switch languages, you should pop your navigation controller to it's root view controller.
|
||||
|
||||
## Credits
|
||||
|
||||
DJLocalization is brought to you by [David Jennes](https://twitter.com/davidjennes). The code is inspired by [Rolandas Razma's](https://twitter.com/rolandas_razma) work with [RRBaseInternationalization](https://github.com/RolandasRazma/RRBaseInternationalization) (especially the way to load strings for storyboard/xib views).
|
||||
|
||||
Props to Rolandas Razma
|
||||
|
||||
## License
|
||||
|
||||
DJLocalization is available under the MIT license. See the LICENSE file for more info.
|
||||
861
Example/Pods/FBSDKCoreKit/FBSDKCoreKit/FBSDKCoreKit/AppEvents/FBSDKAppEvents.h
generated
Normal file
861
Example/Pods/FBSDKCoreKit/FBSDKCoreKit/FBSDKCoreKit/AppEvents/FBSDKAppEvents.h
generated
Normal file
@ -0,0 +1,861 @@
|
||||
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
|
||||
//
|
||||
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
|
||||
// copy, modify, and distribute this software in source code or binary form for use
|
||||
// in connection with the web services and APIs provided by Facebook.
|
||||
//
|
||||
// As with any software that integrates with the Facebook platform, your use of
|
||||
// this software is subject to the Facebook Developer Principles and Policies
|
||||
// [http://developers.facebook.com/policy/]. This copyright 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 <Foundation/Foundation.h>
|
||||
|
||||
#if !TARGET_OS_TV
|
||||
#import <WebKit/WebKit.h>
|
||||
#endif
|
||||
|
||||
#import <FBSDKCoreKit/FBSDKGraphRequestConnection.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class FBSDKAccessToken;
|
||||
@class FBSDKGraphRequest;
|
||||
|
||||
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
|
||||
|
||||
/** NSNotificationCenter name indicating a result of a failed log flush attempt. The posted object will be an NSError instance. */
|
||||
FOUNDATION_EXPORT NSNotificationName const FBSDKAppEventsLoggingResultNotification
|
||||
NS_SWIFT_NAME(AppEventsLoggingResult);
|
||||
|
||||
#else
|
||||
|
||||
/** NSNotificationCenter name indicating a result of a failed log flush attempt. The posted object will be an NSError instance. */
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventsLoggingResultNotification
|
||||
NS_SWIFT_NAME(AppEventsLoggingResultNotification);
|
||||
|
||||
#endif
|
||||
|
||||
/** optional plist key ("FacebookLoggingOverrideAppID") for setting `loggingOverrideAppID` */
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventsOverrideAppIDBundleKey
|
||||
NS_SWIFT_NAME(AppEventsOverrideAppIDBundleKey);
|
||||
|
||||
/**
|
||||
|
||||
NS_ENUM (NSUInteger, FBSDKAppEventsFlushBehavior)
|
||||
|
||||
Specifies when `FBSDKAppEvents` sends log events to the server.
|
||||
|
||||
*/
|
||||
typedef NS_ENUM(NSUInteger, FBSDKAppEventsFlushBehavior)
|
||||
{
|
||||
|
||||
/** Flush automatically: periodically (once a minute or every 100 logged events) and always at app reactivation. */
|
||||
FBSDKAppEventsFlushBehaviorAuto = 0,
|
||||
|
||||
/** Only flush when the `flush` method is called. When an app is moved to background/terminated, the
|
||||
events are persisted and re-established at activation, but they will only be written with an
|
||||
explicit call to `flush`. */
|
||||
FBSDKAppEventsFlushBehaviorExplicitOnly,
|
||||
} NS_SWIFT_NAME(AppEvents.FlushBehavior);
|
||||
|
||||
/**
|
||||
NS_ENUM(NSUInteger, FBSDKProductAvailability)
|
||||
Specifies product availability for Product Catalog product item update
|
||||
*/
|
||||
typedef NS_ENUM(NSUInteger, FBSDKProductAvailability)
|
||||
{
|
||||
/**
|
||||
* Item ships immediately
|
||||
*/
|
||||
FBSDKProductAvailabilityInStock = 0,
|
||||
/**
|
||||
* No plan to restock
|
||||
*/
|
||||
FBSDKProductAvailabilityOutOfStock,
|
||||
/**
|
||||
* Available in future
|
||||
*/
|
||||
FBSDKProductAvailabilityPreOrder,
|
||||
/**
|
||||
* Ships in 1-2 weeks
|
||||
*/
|
||||
FBSDKProductAvailabilityAvailableForOrder,
|
||||
/**
|
||||
* Discontinued
|
||||
*/
|
||||
FBSDKProductAvailabilityDiscontinued,
|
||||
} NS_SWIFT_NAME(AppEvents.ProductAvailability);
|
||||
|
||||
/**
|
||||
NS_ENUM(NSUInteger, FBSDKProductCondition)
|
||||
Specifies product condition for Product Catalog product item update
|
||||
*/
|
||||
typedef NS_ENUM(NSUInteger, FBSDKProductCondition)
|
||||
{
|
||||
FBSDKProductConditionNew = 0,
|
||||
FBSDKProductConditionRefurbished,
|
||||
FBSDKProductConditionUsed,
|
||||
} NS_SWIFT_NAME(AppEvents.ProductCondition);
|
||||
|
||||
/**
|
||||
@methodgroup Predefined event names for logging events common to many apps. Logging occurs through the `logEvent` family of methods on `FBSDKAppEvents`.
|
||||
Common event parameters are provided in the `FBSDKAppEventsParameterNames*` constants.
|
||||
*/
|
||||
|
||||
/// typedef for FBSDKAppEventName
|
||||
typedef NSString *const FBSDKAppEventName NS_TYPED_EXTENSIBLE_ENUM NS_SWIFT_NAME(AppEvents.Name);
|
||||
|
||||
/** Log this event when the user has achieved a level in the app. */
|
||||
FOUNDATION_EXPORT FBSDKAppEventName FBSDKAppEventNameAchievedLevel;
|
||||
|
||||
/** Log this event when the user has entered their payment info. */
|
||||
FOUNDATION_EXPORT FBSDKAppEventName FBSDKAppEventNameAddedPaymentInfo;
|
||||
|
||||
/** Log this event when the user has added an item to their cart. The valueToSum passed to logEvent should be the item's price. */
|
||||
FOUNDATION_EXPORT FBSDKAppEventName FBSDKAppEventNameAddedToCart;
|
||||
|
||||
/** Log this event when the user has added an item to their wishlist. The valueToSum passed to logEvent should be the item's price. */
|
||||
FOUNDATION_EXPORT FBSDKAppEventName FBSDKAppEventNameAddedToWishlist;
|
||||
|
||||
/** Log this event when a user has completed registration with the app. */
|
||||
FOUNDATION_EXPORT FBSDKAppEventName FBSDKAppEventNameCompletedRegistration;
|
||||
|
||||
/** Log this event when the user has completed a tutorial in the app. */
|
||||
FOUNDATION_EXPORT FBSDKAppEventName FBSDKAppEventNameCompletedTutorial;
|
||||
|
||||
/** Log this event when the user has entered the checkout process. The valueToSum passed to logEvent should be the total price in the cart. */
|
||||
FOUNDATION_EXPORT FBSDKAppEventName FBSDKAppEventNameInitiatedCheckout;
|
||||
|
||||
/** Log this event when the user has completed a transaction. The valueToSum passed to logEvent should be the total price of the transaction. */
|
||||
FOUNDATION_EXPORT FBSDKAppEventName FBSDKAppEventNamePurchased;
|
||||
|
||||
/** Log this event when the user has rated an item in the app. The valueToSum passed to logEvent should be the numeric rating. */
|
||||
FOUNDATION_EXPORT FBSDKAppEventName FBSDKAppEventNameRated;
|
||||
|
||||
/** Log this event when a user has performed a search within the app. */
|
||||
FOUNDATION_EXPORT FBSDKAppEventName FBSDKAppEventNameSearched;
|
||||
|
||||
/** Log this event when the user has spent app credits. The valueToSum passed to logEvent should be the number of credits spent. */
|
||||
FOUNDATION_EXPORT FBSDKAppEventName FBSDKAppEventNameSpentCredits;
|
||||
|
||||
/** Log this event when the user has unlocked an achievement in the app. */
|
||||
FOUNDATION_EXPORT FBSDKAppEventName FBSDKAppEventNameUnlockedAchievement;
|
||||
|
||||
/** Log this event when a user has viewed a form of content in the app. */
|
||||
FOUNDATION_EXPORT FBSDKAppEventName FBSDKAppEventNameViewedContent;
|
||||
|
||||
/** A telephone/SMS, email, chat or other type of contact between a customer and your business. */
|
||||
FOUNDATION_EXPORT FBSDKAppEventName FBSDKAppEventNameContact;
|
||||
|
||||
/** The customization of products through a configuration tool or other application your business owns. */
|
||||
FOUNDATION_EXPORT FBSDKAppEventName FBSDKAppEventNameCustomizeProduct;
|
||||
|
||||
/** The donation of funds to your organization or cause. */
|
||||
FOUNDATION_EXPORT FBSDKAppEventName FBSDKAppEventNameDonate;
|
||||
|
||||
/** When a person finds one of your locations via web or application, with an intention to visit (example: find product at a local store). */
|
||||
FOUNDATION_EXPORT FBSDKAppEventName FBSDKAppEventNameFindLocation;
|
||||
|
||||
/** The booking of an appointment to visit one of your locations. */
|
||||
FOUNDATION_EXPORT FBSDKAppEventName FBSDKAppEventNameSchedule;
|
||||
|
||||
/** The subsequent subscriptions after the start of a paid subscription for a product or service you offer. */
|
||||
FOUNDATION_EXPORT FBSDKAppEventName FBSDKAppEventNameSubscriptionHeartbeat __attribute((deprecated("This attribute is no longer used.")));
|
||||
|
||||
/** The start of a free trial of a product or service you offer (example: trial subscription). */
|
||||
FOUNDATION_EXPORT FBSDKAppEventName FBSDKAppEventNameStartTrial;
|
||||
|
||||
/** The submission of an application for a product, service or program you offer (example: credit card, educational program or job). */
|
||||
FOUNDATION_EXPORT FBSDKAppEventName FBSDKAppEventNameSubmitApplication;
|
||||
|
||||
/** The start of a paid subscription for a product or service you offer. */
|
||||
FOUNDATION_EXPORT FBSDKAppEventName FBSDKAppEventNameSubscribe;
|
||||
|
||||
/** Log this event when the user views an ad. */
|
||||
FOUNDATION_EXPORT FBSDKAppEventName FBSDKAppEventNameAdImpression;
|
||||
|
||||
/** Log this event when the user clicks an ad. */
|
||||
FOUNDATION_EXPORT FBSDKAppEventName FBSDKAppEventNameAdClick;
|
||||
|
||||
/**
|
||||
@methodgroup Predefined event name parameters for common additional information to accompany events logged through the `logEvent` family
|
||||
of methods on `FBSDKAppEvents`. Common event names are provided in the `FBAppEventName*` constants.
|
||||
*/
|
||||
|
||||
/// typedef for FBSDKAppEventParameterName
|
||||
typedef NSString *const FBSDKAppEventParameterName NS_TYPED_EXTENSIBLE_ENUM NS_SWIFT_NAME(AppEvents.ParameterName);
|
||||
|
||||
/**
|
||||
* Parameter key used to specify data for the one or more pieces of content being logged about.
|
||||
* Data should be a JSON encoded string.
|
||||
* Example:
|
||||
* "[{\"id\": \"1234\", \"quantity\": 2, \"item_price\": 5.99}, {\"id\": \"5678\", \"quantity\": 1, \"item_price\": 9.99}]"
|
||||
*/
|
||||
FOUNDATION_EXPORT FBSDKAppEventParameterName FBSDKAppEventParameterNameContent;
|
||||
|
||||
/** Parameter key used to specify an ID for the specific piece of content being logged about. Could be an EAN, article identifier, etc., depending on the nature of the app. */
|
||||
FOUNDATION_EXPORT FBSDKAppEventParameterName FBSDKAppEventParameterNameContentID;
|
||||
|
||||
/** Parameter key used to specify a generic content type/family for the logged event, e.g. "music", "photo", "video". Options to use will vary based upon what the app is all about. */
|
||||
FOUNDATION_EXPORT FBSDKAppEventParameterName FBSDKAppEventParameterNameContentType;
|
||||
|
||||
/** Parameter key used to specify currency used with logged event. E.g. "USD", "EUR", "GBP". See ISO-4217 for specific values. One reference for these is <http://en.wikipedia.org/wiki/ISO_4217>. */
|
||||
FOUNDATION_EXPORT FBSDKAppEventParameterName FBSDKAppEventParameterNameCurrency;
|
||||
|
||||
/** Parameter key used to specify a description appropriate to the event being logged. E.g., the name of the achievement unlocked in the `FBAppEventNameAchievementUnlocked` event. */
|
||||
FOUNDATION_EXPORT FBSDKAppEventParameterName FBSDKAppEventParameterNameDescription;
|
||||
|
||||
/** Parameter key used to specify the level achieved in a `FBAppEventNameAchieved` event. */
|
||||
FOUNDATION_EXPORT FBSDKAppEventParameterName FBSDKAppEventParameterNameLevel;
|
||||
|
||||
/** Parameter key used to specify the maximum rating available for the `FBAppEventNameRate` event. E.g., "5" or "10". */
|
||||
FOUNDATION_EXPORT FBSDKAppEventParameterName FBSDKAppEventParameterNameMaxRatingValue;
|
||||
|
||||
/** Parameter key used to specify how many items are being processed for an `FBAppEventNameInitiatedCheckout` or `FBAppEventNamePurchased` event. */
|
||||
FOUNDATION_EXPORT FBSDKAppEventParameterName FBSDKAppEventParameterNameNumItems;
|
||||
|
||||
/** Parameter key used to specify whether payment info is available for the `FBAppEventNameInitiatedCheckout` event. `FBSDKAppEventParameterValueYes` and `FBSDKAppEventParameterValueNo` are good canonical values to use for this parameter. */
|
||||
FOUNDATION_EXPORT FBSDKAppEventParameterName FBSDKAppEventParameterNamePaymentInfoAvailable;
|
||||
|
||||
/** Parameter key used to specify method user has used to register for the app, e.g., "Facebook", "email", "Twitter", etc */
|
||||
FOUNDATION_EXPORT FBSDKAppEventParameterName FBSDKAppEventParameterNameRegistrationMethod;
|
||||
|
||||
/** Parameter key used to specify the string provided by the user for a search operation. */
|
||||
FOUNDATION_EXPORT FBSDKAppEventParameterName FBSDKAppEventParameterNameSearchString;
|
||||
|
||||
/** Parameter key used to specify whether the activity being logged about was successful or not. `FBSDKAppEventParameterValueYes` and `FBSDKAppEventParameterValueNo` are good canonical values to use for this parameter. */
|
||||
FOUNDATION_EXPORT FBSDKAppEventParameterName FBSDKAppEventParameterNameSuccess;
|
||||
|
||||
/**
|
||||
@methodgroup Predefined event name parameters for common additional information to accompany events logged through the `logProductItem` method on `FBSDKAppEvents`.
|
||||
*/
|
||||
|
||||
/// typedef for FBSDKAppEventParameterProduct
|
||||
typedef NSString *const FBSDKAppEventParameterProduct NS_TYPED_EXTENSIBLE_ENUM NS_SWIFT_NAME(AppEvents.ParameterProduct);
|
||||
|
||||
/** Parameter key used to specify the product item's category. */
|
||||
FOUNDATION_EXPORT FBSDKAppEventParameterProduct FBSDKAppEventParameterProductCategory;
|
||||
|
||||
/** Parameter key used to specify the product item's custom label 0. */
|
||||
FOUNDATION_EXPORT FBSDKAppEventParameterProduct FBSDKAppEventParameterProductCustomLabel0;
|
||||
|
||||
/** Parameter key used to specify the product item's custom label 1. */
|
||||
FOUNDATION_EXPORT FBSDKAppEventParameterProduct FBSDKAppEventParameterProductCustomLabel1;
|
||||
|
||||
/** Parameter key used to specify the product item's custom label 2. */
|
||||
FOUNDATION_EXPORT FBSDKAppEventParameterProduct FBSDKAppEventParameterProductCustomLabel2;
|
||||
|
||||
/** Parameter key used to specify the product item's custom label 3. */
|
||||
FOUNDATION_EXPORT FBSDKAppEventParameterProduct FBSDKAppEventParameterProductCustomLabel3;
|
||||
|
||||
/** Parameter key used to specify the product item's custom label 4. */
|
||||
FOUNDATION_EXPORT FBSDKAppEventParameterProduct FBSDKAppEventParameterProductCustomLabel4;
|
||||
|
||||
/** Parameter key used to specify the product item's AppLink app URL for iOS. */
|
||||
FOUNDATION_EXPORT FBSDKAppEventParameterProduct FBSDKAppEventParameterProductAppLinkIOSUrl;
|
||||
|
||||
/** Parameter key used to specify the product item's AppLink app ID for iOS App Store. */
|
||||
FOUNDATION_EXPORT FBSDKAppEventParameterProduct FBSDKAppEventParameterProductAppLinkIOSAppStoreID;
|
||||
|
||||
/** Parameter key used to specify the product item's AppLink app name for iOS. */
|
||||
FOUNDATION_EXPORT FBSDKAppEventParameterProduct FBSDKAppEventParameterProductAppLinkIOSAppName;
|
||||
|
||||
/** Parameter key used to specify the product item's AppLink app URL for iPhone. */
|
||||
FOUNDATION_EXPORT FBSDKAppEventParameterProduct FBSDKAppEventParameterProductAppLinkIPhoneUrl;
|
||||
|
||||
/** Parameter key used to specify the product item's AppLink app ID for iPhone App Store. */
|
||||
FOUNDATION_EXPORT FBSDKAppEventParameterProduct FBSDKAppEventParameterProductAppLinkIPhoneAppStoreID;
|
||||
|
||||
/** Parameter key used to specify the product item's AppLink app name for iPhone. */
|
||||
FOUNDATION_EXPORT FBSDKAppEventParameterProduct FBSDKAppEventParameterProductAppLinkIPhoneAppName;
|
||||
|
||||
/** Parameter key used to specify the product item's AppLink app URL for iPad. */
|
||||
FOUNDATION_EXPORT FBSDKAppEventParameterProduct FBSDKAppEventParameterProductAppLinkIPadUrl;
|
||||
|
||||
/** Parameter key used to specify the product item's AppLink app ID for iPad App Store. */
|
||||
FOUNDATION_EXPORT FBSDKAppEventParameterProduct FBSDKAppEventParameterProductAppLinkIPadAppStoreID;
|
||||
|
||||
/** Parameter key used to specify the product item's AppLink app name for iPad. */
|
||||
FOUNDATION_EXPORT FBSDKAppEventParameterProduct FBSDKAppEventParameterProductAppLinkIPadAppName;
|
||||
|
||||
/** Parameter key used to specify the product item's AppLink app URL for Android. */
|
||||
FOUNDATION_EXPORT FBSDKAppEventParameterProduct FBSDKAppEventParameterProductAppLinkAndroidUrl;
|
||||
|
||||
/** Parameter key used to specify the product item's AppLink fully-qualified package name for intent generation. */
|
||||
FOUNDATION_EXPORT FBSDKAppEventParameterProduct FBSDKAppEventParameterProductAppLinkAndroidPackage;
|
||||
|
||||
/** Parameter key used to specify the product item's AppLink app name for Android. */
|
||||
FOUNDATION_EXPORT FBSDKAppEventParameterProduct FBSDKAppEventParameterProductAppLinkAndroidAppName;
|
||||
|
||||
/** Parameter key used to specify the product item's AppLink app URL for Windows Phone. */
|
||||
FOUNDATION_EXPORT FBSDKAppEventParameterProduct FBSDKAppEventParameterProductAppLinkWindowsPhoneUrl;
|
||||
|
||||
/** Parameter key used to specify the product item's AppLink app ID, as a GUID, for App Store. */
|
||||
FOUNDATION_EXPORT FBSDKAppEventParameterProduct FBSDKAppEventParameterProductAppLinkWindowsPhoneAppID;
|
||||
|
||||
/** Parameter key used to specify the product item's AppLink app name for Windows Phone. */
|
||||
FOUNDATION_EXPORT FBSDKAppEventParameterProduct FBSDKAppEventParameterProductAppLinkWindowsPhoneAppName;
|
||||
|
||||
/*
|
||||
@methodgroup Predefined values to assign to event parameters that accompany events logged through the `logEvent` family
|
||||
of methods on `FBSDKAppEvents`. Common event parameters are provided in the `FBSDKAppEventParameterName*` constants.
|
||||
*/
|
||||
|
||||
/// typedef for FBSDKAppEventParameterValue
|
||||
typedef NSString *const FBSDKAppEventParameterValue NS_TYPED_EXTENSIBLE_ENUM NS_SWIFT_NAME(AppEvents.ParameterValue);
|
||||
|
||||
/** Yes-valued parameter value to be used with parameter keys that need a Yes/No value */
|
||||
FOUNDATION_EXPORT FBSDKAppEventParameterValue FBSDKAppEventParameterValueYes;
|
||||
|
||||
/** No-valued parameter value to be used with parameter keys that need a Yes/No value */
|
||||
FOUNDATION_EXPORT FBSDKAppEventParameterValue FBSDKAppEventParameterValueNo;
|
||||
|
||||
/** Parameter key used to specify the type of ad in an FBSDKAppEventNameAdImpression
|
||||
* or FBSDKAppEventNameAdClick event.
|
||||
* E.g. "banner", "interstitial", "rewarded_video", "native" */
|
||||
FOUNDATION_EXPORT FBSDKAppEventParameterName FBSDKAppEventParameterNameAdType;
|
||||
|
||||
/** Parameter key used to specify the unique ID for all events within a subscription
|
||||
* in an FBSDKAppEventNameSubscribe or FBSDKAppEventNameStartTrial event. */
|
||||
FOUNDATION_EXPORT FBSDKAppEventParameterName FBSDKAppEventParameterNameOrderID;
|
||||
|
||||
/*
|
||||
@methodgroup Predefined values to assign to user data store
|
||||
*/
|
||||
|
||||
/// typedef for FBSDKAppEventUserDataType
|
||||
typedef NSString *const FBSDKAppEventUserDataType NS_TYPED_EXTENSIBLE_ENUM NS_SWIFT_NAME(AppEvents.UserDataType);
|
||||
|
||||
/** Parameter key used to specify user's email. */
|
||||
FOUNDATION_EXPORT FBSDKAppEventUserDataType FBSDKAppEventEmail;
|
||||
|
||||
/** Parameter key used to specify user's first name. */
|
||||
FOUNDATION_EXPORT FBSDKAppEventUserDataType FBSDKAppEventFirstName;
|
||||
|
||||
/** Parameter key used to specify user's last name. */
|
||||
FOUNDATION_EXPORT FBSDKAppEventUserDataType FBSDKAppEventLastName;
|
||||
|
||||
/** Parameter key used to specify user's phone. */
|
||||
FOUNDATION_EXPORT FBSDKAppEventUserDataType FBSDKAppEventPhone;
|
||||
|
||||
/** Parameter key used to specify user's date of birth. */
|
||||
FOUNDATION_EXPORT FBSDKAppEventUserDataType FBSDKAppEventDateOfBirth;
|
||||
|
||||
/** Parameter key used to specify user's gender. */
|
||||
FOUNDATION_EXPORT FBSDKAppEventUserDataType FBSDKAppEventGender;
|
||||
|
||||
/** Parameter key used to specify user's city. */
|
||||
FOUNDATION_EXPORT FBSDKAppEventUserDataType FBSDKAppEventCity;
|
||||
|
||||
/** Parameter key used to specify user's state. */
|
||||
FOUNDATION_EXPORT FBSDKAppEventUserDataType FBSDKAppEventState;
|
||||
|
||||
/** Parameter key used to specify user's zip. */
|
||||
FOUNDATION_EXPORT FBSDKAppEventUserDataType FBSDKAppEventZip;
|
||||
|
||||
/** Parameter key used to specify user's country. */
|
||||
FOUNDATION_EXPORT FBSDKAppEventUserDataType FBSDKAppEventCountry;
|
||||
|
||||
/**
|
||||
|
||||
|
||||
Client-side event logging for specialized application analytics available through Facebook App Insights
|
||||
and for use with Facebook Ads conversion tracking and optimization.
|
||||
|
||||
|
||||
|
||||
The `FBSDKAppEvents` static class has a few related roles:
|
||||
|
||||
+ Logging predefined and application-defined events to Facebook App Insights with a
|
||||
numeric value to sum across a large number of events, and an optional set of key/value
|
||||
parameters that define "segments" for this event (e.g., 'purchaserStatus' : 'frequent', or
|
||||
'gamerLevel' : 'intermediate')
|
||||
|
||||
+ Logging events to later be used for ads optimization around lifetime value.
|
||||
|
||||
+ Methods that control the way in which events are flushed out to the Facebook servers.
|
||||
|
||||
Here are some important characteristics of the logging mechanism provided by `FBSDKAppEvents`:
|
||||
|
||||
+ Events are not sent immediately when logged. They're cached and flushed out to the Facebook servers
|
||||
in a number of situations:
|
||||
- when an event count threshold is passed (currently 100 logged events).
|
||||
- when a time threshold is passed (currently 15 seconds).
|
||||
- when an app has gone to background and is then brought back to the foreground.
|
||||
|
||||
+ Events will be accumulated when the app is in a disconnected state, and sent when the connection is
|
||||
restored and one of the above 'flush' conditions are met.
|
||||
|
||||
+ The `FBSDKAppEvents` class is thread-safe in that events may be logged from any of the app's threads.
|
||||
|
||||
+ The developer can set the `flushBehavior` on `FBSDKAppEvents` to force the flushing of events to only
|
||||
occur on an explicit call to the `flush` method.
|
||||
|
||||
+ The developer can turn on console debug output for event logging and flushing to the server by using
|
||||
the `FBSDKLoggingBehaviorAppEvents` value in `[FBSettings setLoggingBehavior:]`.
|
||||
|
||||
Some things to note when logging events:
|
||||
|
||||
+ There is a limit on the number of unique event names an app can use, on the order of 1000.
|
||||
+ There is a limit to the number of unique parameter names in the provided parameters that can
|
||||
be used per event, on the order of 25. This is not just for an individual call, but for all
|
||||
invocations for that eventName.
|
||||
+ Event names and parameter names (the keys in the NSDictionary) must be between 2 and 40 characters, and
|
||||
must consist of alphanumeric characters, _, -, or spaces.
|
||||
+ The length of each parameter value can be no more than on the order of 100 characters.
|
||||
|
||||
*/
|
||||
|
||||
NS_SWIFT_NAME(AppEvents)
|
||||
@interface FBSDKAppEvents : NSObject
|
||||
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
+ (instancetype)new NS_UNAVAILABLE;
|
||||
|
||||
/*
|
||||
* Control over event batching/flushing
|
||||
*/
|
||||
|
||||
/**
|
||||
|
||||
The current event flushing behavior specifying when events are sent back to Facebook servers.
|
||||
*/
|
||||
@property (class, nonatomic, assign) FBSDKAppEventsFlushBehavior flushBehavior;
|
||||
|
||||
/**
|
||||
Set the 'override' App ID for App Event logging.
|
||||
|
||||
|
||||
|
||||
In some cases, apps want to use one Facebook App ID for login and social presence and another
|
||||
for App Event logging. (An example is if multiple apps from the same company share an app ID for login, but
|
||||
want distinct logging.) By default, this value is `nil`, and defers to the `FBSDKAppEventsOverrideAppIDBundleKey`
|
||||
plist value. If that's not set, it defaults to `[FBSDKSettings appID]`.
|
||||
|
||||
This should be set before any other calls are made to `FBSDKAppEvents`. Thus, you should set it in your application
|
||||
delegate's `application:didFinishLaunchingWithOptions:` delegate.
|
||||
*/
|
||||
@property (class, nonatomic, copy, nullable) NSString *loggingOverrideAppID;
|
||||
|
||||
/*
|
||||
The custom user ID to associate with all app events.
|
||||
|
||||
The userID is persisted until it is cleared by passing nil.
|
||||
*/
|
||||
@property (class, nonatomic, copy, nullable) NSString *userID;
|
||||
|
||||
/*
|
||||
* Basic event logging
|
||||
*/
|
||||
|
||||
/**
|
||||
|
||||
Log an event with just an eventName.
|
||||
|
||||
@param eventName The name of the event to record. Limitations on number of events and name length
|
||||
are given in the `FBSDKAppEvents` documentation.
|
||||
|
||||
*/
|
||||
+ (void)logEvent:(FBSDKAppEventName)eventName;
|
||||
|
||||
/**
|
||||
|
||||
Log an event with an eventName and a numeric value to be aggregated with other events of this name.
|
||||
|
||||
@param eventName The name of the event to record. Limitations on number of events and name length
|
||||
are given in the `FBSDKAppEvents` documentation. Common event names are provided in `FBAppEventName*` constants.
|
||||
|
||||
@param valueToSum Amount to be aggregated into all events of this eventName, and App Insights will report
|
||||
the cumulative and average value of this amount.
|
||||
*/
|
||||
+ (void)logEvent:(FBSDKAppEventName)eventName
|
||||
valueToSum:(double)valueToSum;
|
||||
|
||||
|
||||
/**
|
||||
|
||||
Log an event with an eventName and a set of key/value pairs in the parameters dictionary.
|
||||
Parameter limitations are described above.
|
||||
|
||||
@param eventName The name of the event to record. Limitations on number of events and name construction
|
||||
are given in the `FBSDKAppEvents` documentation. Common event names are provided in `FBAppEventName*` constants.
|
||||
|
||||
@param parameters Arbitrary parameter dictionary of characteristics. The keys to this dictionary must
|
||||
be NSString's, and the values are expected to be NSString or NSNumber. Limitations on the number of
|
||||
parameters and name construction are given in the `FBSDKAppEvents` documentation. Commonly used parameter names
|
||||
are provided in `FBSDKAppEventParameterName*` constants.
|
||||
*/
|
||||
+ (void)logEvent:(FBSDKAppEventName)eventName
|
||||
parameters:(NSDictionary<FBSDKAppEventParameterName, id> *)parameters;
|
||||
|
||||
/**
|
||||
|
||||
Log an event with an eventName, a numeric value to be aggregated with other events of this name,
|
||||
and a set of key/value pairs in the parameters dictionary.
|
||||
|
||||
@param eventName The name of the event to record. Limitations on number of events and name construction
|
||||
are given in the `FBSDKAppEvents` documentation. Common event names are provided in `FBAppEventName*` constants.
|
||||
|
||||
@param valueToSum Amount to be aggregated into all events of this eventName, and App Insights will report
|
||||
the cumulative and average value of this amount.
|
||||
|
||||
@param parameters Arbitrary parameter dictionary of characteristics. The keys to this dictionary must
|
||||
be NSString's, and the values are expected to be NSString or NSNumber. Limitations on the number of
|
||||
parameters and name construction are given in the `FBSDKAppEvents` documentation. Commonly used parameter names
|
||||
are provided in `FBSDKAppEventParameterName*` constants.
|
||||
|
||||
*/
|
||||
+ (void)logEvent:(FBSDKAppEventName)eventName
|
||||
valueToSum:(double)valueToSum
|
||||
parameters:(NSDictionary<FBSDKAppEventParameterName, id> *)parameters;
|
||||
|
||||
|
||||
/**
|
||||
|
||||
Log an event with an eventName, a numeric value to be aggregated with other events of this name,
|
||||
and a set of key/value pairs in the parameters dictionary. Providing session lets the developer
|
||||
target a particular <FBSession>. If nil is provided, then `[FBSession activeSession]` will be used.
|
||||
|
||||
@param eventName The name of the event to record. Limitations on number of events and name construction
|
||||
are given in the `FBSDKAppEvents` documentation. Common event names are provided in `FBAppEventName*` constants.
|
||||
|
||||
@param valueToSum Amount to be aggregated into all events of this eventName, and App Insights will report
|
||||
the cumulative and average value of this amount. Note that this is an NSNumber, and a value of `nil` denotes
|
||||
that this event doesn't have a value associated with it for summation.
|
||||
|
||||
@param parameters Arbitrary parameter dictionary of characteristics. The keys to this dictionary must
|
||||
be NSString's, and the values are expected to be NSString or NSNumber. Limitations on the number of
|
||||
parameters and name construction are given in the `FBSDKAppEvents` documentation. Commonly used parameter names
|
||||
are provided in `FBSDKAppEventParameterName*` constants.
|
||||
|
||||
@param accessToken The optional access token to log the event as.
|
||||
*/
|
||||
+ (void)logEvent:(FBSDKAppEventName)eventName
|
||||
valueToSum:(nullable NSNumber *)valueToSum
|
||||
parameters:(NSDictionary<FBSDKAppEventParameterName, id> *)parameters
|
||||
accessToken:(nullable FBSDKAccessToken *)accessToken;
|
||||
|
||||
/*
|
||||
* Purchase logging
|
||||
*/
|
||||
|
||||
/**
|
||||
|
||||
Log a purchase of the specified amount, in the specified currency.
|
||||
|
||||
@param purchaseAmount Purchase amount to be logged, as expressed in the specified currency. This value
|
||||
will be rounded to the thousandths place (e.g., 12.34567 becomes 12.346).
|
||||
|
||||
@param currency Currency, is denoted as, e.g. "USD", "EUR", "GBP". See ISO-4217 for
|
||||
specific values. One reference for these is <http://en.wikipedia.org/wiki/ISO_4217>.
|
||||
|
||||
|
||||
This event immediately triggers a flush of the `FBSDKAppEvents` event queue, unless the `flushBehavior` is set
|
||||
to `FBSDKAppEventsFlushBehaviorExplicitOnly`.
|
||||
|
||||
*/
|
||||
+ (void)logPurchase:(double)purchaseAmount
|
||||
currency:(NSString *)currency;
|
||||
|
||||
/**
|
||||
|
||||
Log a purchase of the specified amount, in the specified currency, also providing a set of
|
||||
additional characteristics describing the purchase.
|
||||
|
||||
@param purchaseAmount Purchase amount to be logged, as expressed in the specified currency.This value
|
||||
will be rounded to the thousandths place (e.g., 12.34567 becomes 12.346).
|
||||
|
||||
@param currency Currency, is denoted as, e.g. "USD", "EUR", "GBP". See ISO-4217 for
|
||||
specific values. One reference for these is <http://en.wikipedia.org/wiki/ISO_4217>.
|
||||
|
||||
@param parameters Arbitrary parameter dictionary of characteristics. The keys to this dictionary must
|
||||
be NSString's, and the values are expected to be NSString or NSNumber. Limitations on the number of
|
||||
parameters and name construction are given in the `FBSDKAppEvents` documentation. Commonly used parameter names
|
||||
are provided in `FBSDKAppEventParameterName*` constants.
|
||||
|
||||
|
||||
This event immediately triggers a flush of the `FBSDKAppEvents` event queue, unless the `flushBehavior` is set
|
||||
to `FBSDKAppEventsFlushBehaviorExplicitOnly`.
|
||||
|
||||
*/
|
||||
+ (void)logPurchase:(double)purchaseAmount
|
||||
currency:(NSString *)currency
|
||||
parameters:(NSDictionary<NSString *, id> *)parameters;
|
||||
|
||||
/**
|
||||
|
||||
Log a purchase of the specified amount, in the specified currency, also providing a set of
|
||||
additional characteristics describing the purchase, as well as an <FBSession> to log to.
|
||||
|
||||
@param purchaseAmount Purchase amount to be logged, as expressed in the specified currency.This value
|
||||
will be rounded to the thousandths place (e.g., 12.34567 becomes 12.346).
|
||||
|
||||
@param currency Currency, is denoted as, e.g. "USD", "EUR", "GBP". See ISO-4217 for
|
||||
specific values. One reference for these is <http://en.wikipedia.org/wiki/ISO_4217>.
|
||||
|
||||
@param parameters Arbitrary parameter dictionary of characteristics. The keys to this dictionary must
|
||||
be NSString's, and the values are expected to be NSString or NSNumber. Limitations on the number of
|
||||
parameters and name construction are given in the `FBSDKAppEvents` documentation. Commonly used parameter names
|
||||
are provided in `FBSDKAppEventParameterName*` constants.
|
||||
|
||||
@param accessToken The optional access token to log the event as.
|
||||
|
||||
|
||||
This event immediately triggers a flush of the `FBSDKAppEvents` event queue, unless the `flushBehavior` is set
|
||||
to `FBSDKAppEventsFlushBehaviorExplicitOnly`.
|
||||
|
||||
*/
|
||||
+ (void)logPurchase:(double)purchaseAmount
|
||||
currency:(NSString *)currency
|
||||
parameters:(NSDictionary<NSString *, id> *)parameters
|
||||
accessToken:(nullable FBSDKAccessToken *)accessToken;
|
||||
|
||||
|
||||
/*
|
||||
* Push Notifications Logging
|
||||
*/
|
||||
|
||||
/**
|
||||
Log an app event that tracks that the application was open via Push Notification.
|
||||
|
||||
@param payload Notification payload received via `UIApplicationDelegate`.
|
||||
*/
|
||||
+ (void)logPushNotificationOpen:(NSDictionary *)payload;
|
||||
|
||||
/**
|
||||
Log an app event that tracks that a custom action was taken from a push notification.
|
||||
|
||||
@param payload Notification payload received via `UIApplicationDelegate`.
|
||||
@param action Name of the action that was taken.
|
||||
*/
|
||||
+ (void)logPushNotificationOpen:(NSDictionary *)payload action:(NSString *)action;
|
||||
|
||||
/**
|
||||
Uploads product catalog product item as an app event
|
||||
@param itemID Unique ID for the item. Can be a variant for a product.
|
||||
Max size is 100.
|
||||
@param availability If item is in stock. Accepted values are:
|
||||
in stock - Item ships immediately
|
||||
out of stock - No plan to restock
|
||||
preorder - Available in future
|
||||
available for order - Ships in 1-2 weeks
|
||||
discontinued - Discontinued
|
||||
@param condition Product condition: new, refurbished or used.
|
||||
@param description Short text describing product. Max size is 5000.
|
||||
@param imageLink Link to item image used in ad.
|
||||
@param link Link to merchant's site where someone can buy the item.
|
||||
@param title Title of item.
|
||||
@param priceAmount Amount of purchase, in the currency specified by the 'currency'
|
||||
parameter. This value will be rounded to the thousandths place
|
||||
(e.g., 12.34567 becomes 12.346).
|
||||
@param currency Currency used to specify the amount.
|
||||
E.g. "USD", "EUR", "GBP". See ISO-4217 for specific values. One reference for these is <http://en.wikipedia.org/wiki/ISO_4217>
|
||||
@param gtin Global Trade Item Number including UPC, EAN, JAN and ISBN
|
||||
@param mpn Unique manufacture ID for product
|
||||
@param brand Name of the brand
|
||||
Note: Either gtin, mpn or brand is required.
|
||||
@param parameters Optional fields for deep link specification.
|
||||
*/
|
||||
+ (void)logProductItem:(NSString *)itemID
|
||||
availability:(FBSDKProductAvailability)availability
|
||||
condition:(FBSDKProductCondition)condition
|
||||
description:(NSString *)description
|
||||
imageLink:(NSString *)imageLink
|
||||
link:(NSString *)link
|
||||
title:(NSString *)title
|
||||
priceAmount:(double)priceAmount
|
||||
currency:(NSString *)currency
|
||||
gtin:(nullable NSString *)gtin
|
||||
mpn:(nullable NSString *)mpn
|
||||
brand:(nullable NSString *)brand
|
||||
parameters:(nullable NSDictionary<NSString *, id> *)parameters;
|
||||
|
||||
/**
|
||||
|
||||
Notifies the events system that the app has launched and, when appropriate, logs an "activated app" event.
|
||||
This function is called automatically from FBSDKApplicationDelegate applicationDidBecomeActive, unless
|
||||
one overrides 'FacebookAutoLogAppEventsEnabled' key to false in the project info plist file.
|
||||
In case 'FacebookAutoLogAppEventsEnabled' is set to false, then it should typically be placed in the
|
||||
app delegates' `applicationDidBecomeActive:` method.
|
||||
|
||||
This method also takes care of logging the event indicating the first time this app has been launched, which, among other things, is used to
|
||||
track user acquisition and app install ads conversions.
|
||||
|
||||
|
||||
|
||||
`activateApp` will not log an event on every app launch, since launches happen every time the app is backgrounded and then foregrounded.
|
||||
"activated app" events will be logged when the app has not been active for more than 60 seconds. This method also causes a "deactivated app"
|
||||
event to be logged when sessions are "completed", and these events are logged with the session length, with an indication of how much
|
||||
time has elapsed between sessions, and with the number of background/foreground interruptions that session had. This data
|
||||
is all visible in your app's App Events Insights.
|
||||
*/
|
||||
+ (void)activateApp;
|
||||
|
||||
/*
|
||||
* Push Notifications Registration and Uninstall Tracking
|
||||
*/
|
||||
|
||||
/**
|
||||
Sets and sends device token to register the current application for push notifications.
|
||||
|
||||
|
||||
|
||||
Sets and sends a device token from `NSData` representation that you get from `UIApplicationDelegate.-application:didRegisterForRemoteNotificationsWithDeviceToken:`.
|
||||
|
||||
@param deviceToken Device token data.
|
||||
*/
|
||||
+ (void)setPushNotificationsDeviceToken:(NSData *)deviceToken;
|
||||
|
||||
/**
|
||||
Sets and sends device token string to register the current application for push notifications.
|
||||
|
||||
|
||||
|
||||
Sets and sends a device token string
|
||||
|
||||
@param deviceTokenString Device token string.
|
||||
*/
|
||||
+ (void)setPushNotificationsDeviceTokenString:(NSString *)deviceTokenString
|
||||
NS_SWIFT_NAME(setPushNotificationsDeviceToken(_:));
|
||||
|
||||
/**
|
||||
Explicitly kick off flushing of events to Facebook. This is an asynchronous method, but it does initiate an immediate
|
||||
kick off. Server failures will be reported through the NotificationCenter with notification ID `FBSDKAppEventsLoggingResultNotification`.
|
||||
*/
|
||||
+ (void)flush;
|
||||
|
||||
/**
|
||||
Creates a request representing the Graph API call to retrieve a Custom Audience "third party ID" for the app's Facebook user.
|
||||
Callers will send this ID back to their own servers, collect up a set to create a Facebook Custom Audience with,
|
||||
and then use the resultant Custom Audience to target ads.
|
||||
|
||||
The JSON in the request's response will include an "custom_audience_third_party_id" key/value pair, with the value being the ID retrieved.
|
||||
This ID is an encrypted encoding of the Facebook user's ID and the invoking Facebook app ID.
|
||||
Multiple calls with the same user will return different IDs, thus these IDs cannot be used to correlate behavior
|
||||
across devices or applications, and are only meaningful when sent back to Facebook for creating Custom Audiences.
|
||||
|
||||
The ID retrieved represents the Facebook user identified in the following way: if the specified access token is valid,
|
||||
the ID will represent the user associated with that token; otherwise the ID will represent the user logged into the
|
||||
native Facebook app on the device. If there is no native Facebook app, no one is logged into it, or the user has opted out
|
||||
at the iOS level from ad tracking, then a `nil` ID will be returned.
|
||||
|
||||
This method returns `nil` if either the user has opted-out (via iOS) from Ad Tracking, the app itself has limited event usage
|
||||
via the `[FBSDKSettings limitEventAndDataUsage]` flag, or a specific Facebook user cannot be identified.
|
||||
|
||||
@param accessToken The access token to use to establish the user's identity for users logged into Facebook through this app.
|
||||
If `nil`, then the `[FBSDKAccessToken currentAccessToken]` is used.
|
||||
*/
|
||||
+ (nullable FBSDKGraphRequest *)requestForCustomAudienceThirdPartyIDWithAccessToken:(nullable FBSDKAccessToken *)accessToken;
|
||||
|
||||
/*
|
||||
Clears the custom user ID to associate with all app events.
|
||||
*/
|
||||
+ (void)clearUserID;
|
||||
|
||||
/*
|
||||
Sets custom user data to associate with all app events. All user data are hashed
|
||||
and used to match Facebook user from this instance of an application.
|
||||
|
||||
The user data will be persisted between application instances.
|
||||
|
||||
@param email user's email
|
||||
@param firstName user's first name
|
||||
@param lastName user's last name
|
||||
@param phone user's phone
|
||||
@param dateOfBirth user's date of birth
|
||||
@param gender user's gender
|
||||
@param city user's city
|
||||
@param state user's state
|
||||
@param zip user's zip
|
||||
@param country user's country
|
||||
*/
|
||||
+ (void)setUserEmail:(nullable NSString *)email
|
||||
firstName:(nullable NSString *)firstName
|
||||
lastName:(nullable NSString *)lastName
|
||||
phone:(nullable NSString *)phone
|
||||
dateOfBirth:(nullable NSString *)dateOfBirth
|
||||
gender:(nullable NSString *)gender
|
||||
city:(nullable NSString *)city
|
||||
state:(nullable NSString *)state
|
||||
zip:(nullable NSString *)zip
|
||||
country:(nullable NSString *)country
|
||||
NS_SWIFT_NAME(setUser(email:firstName:lastName:phone:dateOfBirth:gender:city:state:zip:country:));
|
||||
|
||||
/*
|
||||
Returns the set user data else nil
|
||||
*/
|
||||
+ (nullable NSString *)getUserData;
|
||||
|
||||
/*
|
||||
Clears the current user data
|
||||
*/
|
||||
+ (void)clearUserData;
|
||||
|
||||
/*
|
||||
Sets custom user data to associate with all app events. All user data are hashed
|
||||
and used to match Facebook user from this instance of an application.
|
||||
|
||||
The user data will be persisted between application instances.
|
||||
|
||||
@param data data
|
||||
@param type data type, e.g. FBSDKAppEventEmail, FBSDKAppEventPhone
|
||||
*/
|
||||
+ (void)setUserData:(nullable NSString *)data
|
||||
forType:(FBSDKAppEventUserDataType)type;
|
||||
|
||||
/*
|
||||
Clears the current user data of certain type
|
||||
*/
|
||||
+ (void)clearUserDataForType:(FBSDKAppEventUserDataType)type;
|
||||
|
||||
/*
|
||||
Sends a request to update the properties for the current user, set by `setUserID:`
|
||||
|
||||
You must call `FBSDKAppEvents setUserID:` before making this call.
|
||||
@param properties the custom user properties
|
||||
@param handler the optional completion handler
|
||||
*/
|
||||
+ (void)updateUserProperties:(NSDictionary<NSString *, id> *)properties handler:(nullable FBSDKGraphRequestBlock)handler;
|
||||
|
||||
#if !TARGET_OS_TV
|
||||
/*
|
||||
Intended to be used as part of a hybrid webapp.
|
||||
If you call this method, the FB SDK will inject a new JavaScript object into your webview.
|
||||
If the FB Pixel is used within the webview, and references the app ID of this app,
|
||||
then it will detect the presence of this injected JavaScript object
|
||||
and pass Pixel events back to the FB SDK for logging using the AppEvents framework.
|
||||
|
||||
@param webView The webview to augment with the additional JavaScript behaviour
|
||||
*/
|
||||
+ (void)augmentHybridWKWebView:(WKWebView *)webView;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Unity helper functions
|
||||
*/
|
||||
|
||||
/**
|
||||
|
||||
Set if the Unity is already initialized
|
||||
|
||||
@param isUnityInit whether Unity is initialized.
|
||||
|
||||
*/
|
||||
+ (void)setIsUnityInit:(BOOL)isUnityInit;
|
||||
|
||||
/*
|
||||
Send event binding to Unity
|
||||
*/
|
||||
+ (void)sendEventBindingsToUnity;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
1453
Example/Pods/FBSDKCoreKit/FBSDKCoreKit/FBSDKCoreKit/AppEvents/FBSDKAppEvents.m
generated
Normal file
1453
Example/Pods/FBSDKCoreKit/FBSDKCoreKit/FBSDKCoreKit/AppEvents/FBSDKAppEvents.m
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -18,10 +18,12 @@
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "FBSDKLikeControl.h"
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface FBSDKLikeControl ()
|
||||
@interface FBSDKMetadataIndexer : NSObject
|
||||
|
||||
- (NSDictionary *)analyticsParameters;
|
||||
+ (void)enable;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
323
Example/Pods/FBSDKCoreKit/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/AAM/FBSDKMetadataIndexer.m
generated
Normal file
323
Example/Pods/FBSDKCoreKit/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/AAM/FBSDKMetadataIndexer.m
generated
Normal file
@ -0,0 +1,323 @@
|
||||
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
|
||||
//
|
||||
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
|
||||
// copy, modify, and distribute this software in source code or binary form for use
|
||||
// in connection with the web services and APIs provided by Facebook.
|
||||
//
|
||||
// As with any software that integrates with the Facebook platform, your use of
|
||||
// this software is subject to the Facebook Developer Principles and Policies
|
||||
// [http://developers.facebook.com/policy/]. This copyright 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 "FBSDKMetadataIndexer.h"
|
||||
|
||||
#import <objc/runtime.h>
|
||||
#import <sys/sysctl.h>
|
||||
#import <sys/utsname.h>
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import <FBSDKCoreKit/FBSDKCoreKit+Internal.h>
|
||||
|
||||
static const int FBSDKMetadataIndexerMaxTextLength = 100;
|
||||
static const int FBSDKMetadataIndexerMaxIndicatorLength = 100;
|
||||
static const int FBSDKMetadataIndexerMaxValue = 5;
|
||||
|
||||
static NSString * const FIELD_K = @"k";
|
||||
static NSString * const FIELD_V = @"v";
|
||||
static NSString * const FIELD_K_DELIMITER = @",";
|
||||
|
||||
FBSDKAppEventUserDataType FBSDKAppEventRule1 = @"r1";
|
||||
FBSDKAppEventUserDataType FBSDKAppEventRule2 = @"r2";
|
||||
|
||||
static NSArray<FBSDKAppEventUserDataType> *FBSDKMetadataIndexerKeys;
|
||||
static NSMutableDictionary<NSString *, NSDictionary<NSString *, NSString *> *> *_rules;
|
||||
static NSMutableDictionary<NSString *, NSMutableArray<NSString *> *> *_store;
|
||||
static dispatch_queue_t serialQueue;
|
||||
|
||||
@implementation FBSDKMetadataIndexer
|
||||
|
||||
+ (void)initialize
|
||||
{
|
||||
FBSDKMetadataIndexerKeys = @[FBSDKAppEventRule1, FBSDKAppEventRule2];
|
||||
serialQueue = dispatch_queue_create("com.facebook.appevents.MetadataIndexer", DISPATCH_QUEUE_SERIAL);
|
||||
}
|
||||
|
||||
+ (void)enable
|
||||
{
|
||||
if (FBSDKAdvertisingTrackingAllowed != [FBSDKAppEventsUtility advertisingTrackingStatus]) {
|
||||
return;
|
||||
}
|
||||
[FBSDKServerConfigurationManager loadServerConfigurationWithCompletionBlock:^(FBSDKServerConfiguration *serverConfiguration, NSError *error) {
|
||||
if (error) {
|
||||
return;
|
||||
}
|
||||
[FBSDKMetadataIndexer setupWithRules:serverConfiguration.AAMRules];
|
||||
}];
|
||||
}
|
||||
|
||||
+ (void)setupWithRules:(NSDictionary<NSString *, id> * _Nullable)rules
|
||||
{
|
||||
if (0 == rules.count) {
|
||||
return;
|
||||
}
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
[FBSDKMetadataIndexer constructRules:rules];
|
||||
[FBSDKMetadataIndexer initStore];
|
||||
|
||||
BOOL isEnabled = NO;
|
||||
for (NSString *key in FBSDKMetadataIndexerKeys) {
|
||||
BOOL isRuleEnabled = (nil != [_rules objectForKey:key]);
|
||||
if (isRuleEnabled) {
|
||||
isEnabled = YES;
|
||||
}
|
||||
if (!isRuleEnabled) {
|
||||
[_store removeObjectForKey:key];
|
||||
[FBSDKUserDataStore setHashData:nil forType:key];
|
||||
}
|
||||
}
|
||||
|
||||
if (isEnabled) {
|
||||
[FBSDKMetadataIndexer setupMetadataIndexing];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
+ (void)initStore
|
||||
{
|
||||
_store = [[NSMutableDictionary alloc] init];
|
||||
for (NSString *key in FBSDKMetadataIndexerKeys) {
|
||||
NSString *data = [FBSDKUserDataStore getHashedDataForType:key];
|
||||
if (data.length > 0) {
|
||||
_store[key] = [NSMutableArray arrayWithArray:[data componentsSeparatedByString:FIELD_K_DELIMITER]];
|
||||
}
|
||||
}
|
||||
|
||||
for (NSString *key in FBSDKMetadataIndexerKeys) {
|
||||
if (!_store[key]) {
|
||||
_store[key] = [[NSMutableArray alloc] init];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+ (void)constructRules:(NSDictionary<NSString *, id> * _Nullable)rules
|
||||
{
|
||||
if (!_rules) {
|
||||
_rules = [[NSMutableDictionary alloc] init];
|
||||
}
|
||||
|
||||
for (NSString *key in rules) {
|
||||
NSDictionary<NSString *, NSString *> *value = [FBSDKTypeUtility dictionaryValue:rules[key]];
|
||||
if (value && value[FIELD_K].length > 0 && value[FIELD_V].length > 0) {
|
||||
_rules[key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+ (void)setupMetadataIndexing
|
||||
{
|
||||
void (^block)(UIView *) = ^(UIView *view) {
|
||||
// Indexing when the view is removed from window and conforms to UITextInput, and skip UIFieldEditor, which is an internval view of UITextField
|
||||
if (![view window] && ![NSStringFromClass([view class]) isEqualToString:@"UIFieldEditor"] && [view conformsToProtocol:@protocol(UITextInput)]) {
|
||||
NSString *text = [FBSDKViewHierarchy getText:view];
|
||||
NSString *placeholder = [FBSDKViewHierarchy getHint:view];
|
||||
BOOL secureTextEntry = [self checkSecureTextEntry:view];
|
||||
NSArray<NSString *> *labels = [self getLabelsOfView:view];
|
||||
UIKeyboardType keyboardType = [self getKeyboardType:view];
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {
|
||||
[self getMetadataWithText:[self normalizedValue:text]
|
||||
placeholder:[self normalizeField:placeholder]
|
||||
labels:labels
|
||||
secureTextEntry:secureTextEntry
|
||||
inputType:keyboardType];
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
[FBSDKSwizzler swizzleSelector:@selector(didMoveToWindow) onClass:[UIView class] withBlock:block named:@"metadataIndexingUIView"];
|
||||
|
||||
// iOS 12: UITextField implements didMoveToWindow without calling parent implementation
|
||||
if (@available(iOS 12, *)) {
|
||||
[FBSDKSwizzler swizzleSelector:@selector(didMoveToWindow) onClass:[UITextField class] withBlock:block named:@"metadataIndexingUITextField"];
|
||||
} else {
|
||||
[FBSDKSwizzler swizzleSelector:@selector(didMoveToWindow) onClass:[UIControl class] withBlock:block named:@"metadataIndexingUIControl"];
|
||||
}
|
||||
}
|
||||
|
||||
+ (NSArray<UIView *> *)getSiblingViewsOfView:(UIView *)view
|
||||
{
|
||||
NSObject *parent = [FBSDKViewHierarchy getParent:view];
|
||||
if (parent) {
|
||||
NSArray<id> *views = [FBSDKViewHierarchy getChildren:parent];
|
||||
if (views) {
|
||||
NSMutableArray<id> *siblings = [NSMutableArray arrayWithArray:views];
|
||||
[siblings removeObject:view];
|
||||
return [siblings copy];
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
+ (NSArray<NSString *> *)getLabelsOfView:(UIView *)view
|
||||
{
|
||||
NSMutableArray<NSString *> *labels = [[NSMutableArray alloc] init];
|
||||
|
||||
NSString *placeholder = [self normalizeField:[FBSDKViewHierarchy getHint:view]];
|
||||
if (placeholder) {
|
||||
[labels addObject:placeholder];
|
||||
}
|
||||
|
||||
NSArray<id> *siblingViews = [self getSiblingViewsOfView:view];
|
||||
for (id sibling in siblingViews) {
|
||||
if ([sibling isKindOfClass:[UILabel class]]) {
|
||||
NSString *text = [self normalizeField:[FBSDKViewHierarchy getText:sibling]];
|
||||
if (text) {
|
||||
[labels addObject:text];
|
||||
}
|
||||
}
|
||||
}
|
||||
return [labels copy];
|
||||
}
|
||||
|
||||
+ (BOOL)checkSecureTextEntry:(UIView *)view
|
||||
{
|
||||
if ([view isKindOfClass:[UITextField class]]) {
|
||||
return ((UITextField *)view).secureTextEntry;
|
||||
}
|
||||
if ([view isKindOfClass:[UITextView class]]) {
|
||||
return ((UITextView *)view).secureTextEntry;
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
+ (UIKeyboardType)getKeyboardType:(UIView *)view
|
||||
{
|
||||
if ([view isKindOfClass:[UITextField class]]) {
|
||||
return ((UITextField *)view).keyboardType;
|
||||
}
|
||||
if ([view isKindOfClass:[UITextView class]]) {
|
||||
return ((UITextView *)view).keyboardType;
|
||||
}
|
||||
|
||||
return UIKeyboardTypeDefault;
|
||||
}
|
||||
|
||||
+ (void)getMetadataWithText:(NSString *)text
|
||||
placeholder:(NSString *)placeholder
|
||||
labels:(NSArray<NSString *> *)labels
|
||||
secureTextEntry:(BOOL)secureTextEntry
|
||||
inputType:(UIKeyboardType)inputType
|
||||
{
|
||||
if (secureTextEntry ||
|
||||
[placeholder containsString:@"password"] ||
|
||||
text.length == 0 ||
|
||||
text.length > FBSDKMetadataIndexerMaxTextLength ||
|
||||
placeholder.length >= FBSDKMetadataIndexerMaxIndicatorLength) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (NSString *key in _rules) {
|
||||
NSDictionary<NSString *, NSString *> *rule = _rules[key];
|
||||
BOOL isRuleKMatched = [self checkMetadataHint:placeholder matchRuleK:rule[FIELD_K]]
|
||||
|| [self checkMetadataLabels:labels matchRuleK:rule[FIELD_K]];
|
||||
BOOL isRuleVMatched = [self checkMetadataText:text matchRuleV:rule[FIELD_V]];
|
||||
if (isRuleKMatched && isRuleVMatched) {
|
||||
[FBSDKMetadataIndexer checkAndAppendData:text forKey:key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Helper Methods
|
||||
|
||||
+ (void)checkAndAppendData:(NSString *)data
|
||||
forKey:(NSString *)key
|
||||
{
|
||||
NSString *hashData = [FBSDKUtility SHA256Hash:data];
|
||||
dispatch_async(serialQueue, ^{
|
||||
if (hashData.length == 0 || [_store[key] containsObject:hashData]) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (_store[key].count >= FBSDKMetadataIndexerMaxValue) {
|
||||
[_store[key] removeObjectAtIndex:0];
|
||||
}
|
||||
[_store[key] addObject:hashData];
|
||||
[FBSDKUserDataStore setHashData:[_store[key] componentsJoinedByString:@","]
|
||||
forType:key];
|
||||
});
|
||||
}
|
||||
|
||||
+ (BOOL)checkMetadataLabels:(NSArray<NSString *> *)labels
|
||||
matchRuleK:(NSString *)ruleK
|
||||
{
|
||||
for (NSString *label in labels) {
|
||||
if ([self checkMetadataHint:label matchRuleK:ruleK]) {
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
+ (BOOL)checkMetadataHint:(NSString *)hint
|
||||
matchRuleK:(NSString *)ruleK
|
||||
{
|
||||
if (hint.length > 0 && ruleK) {
|
||||
NSArray<NSString *> *items = [ruleK componentsSeparatedByString:@","];
|
||||
for (NSString *item in items) {
|
||||
if ([hint containsString:item]) {
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
+ (BOOL)checkMetadataText:(NSString *)text
|
||||
matchRuleV:(NSString *)ruleV
|
||||
{
|
||||
if (text.length > 0 && ruleV) {
|
||||
NSRegularExpression *regex = [[NSRegularExpression alloc] initWithPattern:ruleV
|
||||
options:NSRegularExpressionCaseInsensitive
|
||||
error:nil];
|
||||
NSUInteger matches = [regex numberOfMatchesInString:text options:0 range:NSMakeRange(0, text.length)];
|
||||
|
||||
NSString *prunedText = [[text componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"+- ()."]] componentsJoinedByString:@""];
|
||||
NSUInteger prunedMatches = [regex numberOfMatchesInString:prunedText options:0 range:NSMakeRange(0, prunedText.length)];
|
||||
|
||||
return matches > 0 || prunedMatches > 0;
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
+ (NSString *)normalizeField:(NSString *)field
|
||||
{
|
||||
if (!field) {
|
||||
return nil;
|
||||
}
|
||||
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"[_-]|\\s"
|
||||
options:NSRegularExpressionCaseInsensitive
|
||||
error:nil];
|
||||
return [regex stringByReplacingMatchesInString:field
|
||||
options:0
|
||||
range:NSMakeRange(0, field.length)
|
||||
withTemplate:@""].lowercaseString;
|
||||
}
|
||||
|
||||
+ (NSString *)normalizedValue:(NSString *)value
|
||||
{
|
||||
if (!value) {
|
||||
return nil;
|
||||
}
|
||||
return [value stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]].lowercaseString;
|
||||
}
|
||||
|
||||
@end
|
||||
@ -0,0 +1,34 @@
|
||||
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
|
||||
//
|
||||
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
|
||||
// copy, modify, and distribute this software in source code or binary form for use
|
||||
// in connection with the web services and APIs provided by Facebook.
|
||||
//
|
||||
// As with any software that integrates with the Facebook platform, your use of
|
||||
// this software is subject to the Facebook Developer Principles and Policies
|
||||
// [http://developers.facebook.com/policy/]. This copyright 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 <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
typedef void(^FBSDKCodelessSettingLoadBlock)(BOOL isCodelessSetupEnabled, NSError *_Nullable error);
|
||||
|
||||
NS_SWIFT_NAME(CodelessIndexer)
|
||||
@interface FBSDKCodelessIndexer : NSObject
|
||||
|
||||
@property (class, nonatomic, copy, readonly) NSString *extInfo;
|
||||
|
||||
+ (void)enable;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
408
Example/Pods/FBSDKCoreKit/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/Codeless/FBSDKCodelessIndexer.m
generated
Normal file
408
Example/Pods/FBSDKCoreKit/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/Codeless/FBSDKCodelessIndexer.m
generated
Normal file
@ -0,0 +1,408 @@
|
||||
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
|
||||
//
|
||||
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
|
||||
// copy, modify, and distribute this software in source code or binary form for use
|
||||
// in connection with the web services and APIs provided by Facebook.
|
||||
//
|
||||
// As with any software that integrates with the Facebook platform, your use of
|
||||
// this software is subject to the Facebook Developer Principles and Policies
|
||||
// [http://developers.facebook.com/policy/]. This copyright 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 "FBSDKCodelessIndexer.h"
|
||||
|
||||
#import <objc/runtime.h>
|
||||
#import <sys/sysctl.h>
|
||||
#import <sys/utsname.h>
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import <FBSDKCoreKit/FBSDKCoreKit+Internal.h>
|
||||
#import <FBSDKCoreKit/FBSDKGraphRequest.h>
|
||||
#import <FBSDKCoreKit/FBSDKSettings.h>
|
||||
|
||||
@implementation FBSDKCodelessIndexer
|
||||
|
||||
static BOOL _isCodelessIndexing;
|
||||
static BOOL _isCheckingSession;
|
||||
static BOOL _isCodelessIndexingEnabled;
|
||||
static BOOL _isGestureSet;
|
||||
|
||||
static NSMutableDictionary<NSString *, id> *_codelessSetting;
|
||||
static const NSTimeInterval kTimeout = 4.0;
|
||||
|
||||
static NSString *_deviceSessionID;
|
||||
static NSTimer *_appIndexingTimer;
|
||||
static NSString *_lastTreeHash;
|
||||
|
||||
+ (void)enable
|
||||
{
|
||||
if (_isGestureSet) {
|
||||
return;
|
||||
}
|
||||
#if TARGET_OS_SIMULATOR
|
||||
[self setupGesture];
|
||||
#else
|
||||
[self loadCodelessSettingWithCompletionBlock:^(BOOL isCodelessSetupEnabled, NSError *error) {
|
||||
if (isCodelessSetupEnabled) {
|
||||
[self setupGesture];
|
||||
}
|
||||
}];
|
||||
#endif
|
||||
}
|
||||
|
||||
// DO NOT call this function, it is only called once in the load function
|
||||
+ (void)loadCodelessSettingWithCompletionBlock:(FBSDKCodelessSettingLoadBlock)completionBlock
|
||||
{
|
||||
NSString *appID = [FBSDKSettings appID];
|
||||
if (appID == nil) {
|
||||
return;
|
||||
}
|
||||
|
||||
[FBSDKServerConfigurationManager loadServerConfigurationWithCompletionBlock:^(FBSDKServerConfiguration *serverConfiguration, NSError *serverConfigurationLoadingError) {
|
||||
if (!serverConfiguration.codelessEventsEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
// load the defaults
|
||||
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
||||
NSString *defaultKey = [NSString stringWithFormat:CODELESS_SETTING_KEY, appID];
|
||||
NSData *data = [defaults objectForKey:defaultKey];
|
||||
if ([data isKindOfClass:[NSData class]]) {
|
||||
NSMutableDictionary<NSString *, id> *codelessSetting = [NSKeyedUnarchiver unarchiveObjectWithData:data];
|
||||
if (codelessSetting) {
|
||||
_codelessSetting = codelessSetting;
|
||||
}
|
||||
}
|
||||
if (!_codelessSetting) {
|
||||
_codelessSetting = [[NSMutableDictionary alloc] init];
|
||||
}
|
||||
|
||||
if (![self _codelessSetupTimestampIsValid:[_codelessSetting objectForKey:CODELESS_SETTING_TIMESTAMP_KEY]]) {
|
||||
FBSDKGraphRequest *request = [self requestToLoadCodelessSetup:appID];
|
||||
if (request == nil) {
|
||||
return;
|
||||
}
|
||||
FBSDKGraphRequestConnection *requestConnection = [[FBSDKGraphRequestConnection alloc] init];
|
||||
requestConnection.timeout = kTimeout;
|
||||
[requestConnection addRequest:request completionHandler:^(FBSDKGraphRequestConnection *connection, id result, NSError *codelessLoadingError) {
|
||||
if (codelessLoadingError) {
|
||||
return;
|
||||
}
|
||||
|
||||
NSDictionary<NSString *, id> *resultDictionary = [FBSDKTypeUtility dictionaryValue:result];
|
||||
if (resultDictionary) {
|
||||
BOOL isCodelessSetupEnabled = [FBSDKTypeUtility boolValue:resultDictionary[CODELESS_SETUP_ENABLED_FIELD]];
|
||||
[_codelessSetting setObject:@(isCodelessSetupEnabled) forKey:CODELESS_SETUP_ENABLED_KEY];
|
||||
[_codelessSetting setObject:[NSDate date] forKey:CODELESS_SETTING_TIMESTAMP_KEY];
|
||||
// update the cached copy in user defaults
|
||||
[defaults setObject:[NSKeyedArchiver archivedDataWithRootObject:_codelessSetting] forKey:defaultKey];
|
||||
completionBlock(isCodelessSetupEnabled, codelessLoadingError);
|
||||
}
|
||||
}];
|
||||
[requestConnection start];
|
||||
} else {
|
||||
completionBlock([FBSDKTypeUtility boolValue:[_codelessSetting objectForKey:CODELESS_SETUP_ENABLED_KEY]], nil);
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
+ (FBSDKGraphRequest *)requestToLoadCodelessSetup:(NSString *)appID
|
||||
{
|
||||
NSString *advertiserID = [FBSDKAppEventsUtility advertiserID];
|
||||
if (!advertiserID) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSDictionary<NSString *, NSString *> *parameters = @{
|
||||
@"fields": CODELESS_SETUP_ENABLED_FIELD,
|
||||
@"advertiser_id": advertiserID
|
||||
};
|
||||
|
||||
FBSDKGraphRequest *request = [[FBSDKGraphRequest alloc] initWithGraphPath:appID
|
||||
parameters:parameters
|
||||
tokenString:nil
|
||||
HTTPMethod:nil
|
||||
flags:FBSDKGraphRequestFlagSkipClientToken | FBSDKGraphRequestFlagDisableErrorRecovery];
|
||||
return request;
|
||||
}
|
||||
|
||||
+ (BOOL)_codelessSetupTimestampIsValid:(NSDate *)timestamp
|
||||
{
|
||||
return (timestamp != nil && [[NSDate date] timeIntervalSinceDate:timestamp] < CODELESS_SETTING_CACHE_TIMEOUT);
|
||||
}
|
||||
|
||||
+ (void)setupGesture
|
||||
{
|
||||
_isGestureSet = YES;
|
||||
[UIApplication sharedApplication].applicationSupportsShakeToEdit = YES;
|
||||
Class class = [UIApplication class];
|
||||
|
||||
[FBSDKSwizzler swizzleSelector:@selector(motionBegan:withEvent:) onClass:class withBlock:^{
|
||||
if ([FBSDKServerConfigurationManager cachedServerConfiguration].isCodelessEventsEnabled) {
|
||||
[self checkCodelessIndexingSession];
|
||||
}
|
||||
} named:@"motionBegan"];
|
||||
}
|
||||
|
||||
+ (void)checkCodelessIndexingSession
|
||||
{
|
||||
if (_isCheckingSession) return;
|
||||
|
||||
_isCheckingSession = YES;
|
||||
NSDictionary *parameters = @{
|
||||
CODELESS_INDEXING_SESSION_ID_KEY: [self currentSessionDeviceID],
|
||||
CODELESS_INDEXING_EXT_INFO_KEY: [self extInfo]
|
||||
};
|
||||
FBSDKGraphRequest *request = [[FBSDKGraphRequest alloc]
|
||||
initWithGraphPath:[NSString stringWithFormat:@"%@/%@",
|
||||
[FBSDKSettings appID], CODELESS_INDEXING_SESSION_ENDPOINT]
|
||||
parameters:parameters
|
||||
HTTPMethod:FBSDKHTTPMethodPOST];
|
||||
[request startWithCompletionHandler:^(FBSDKGraphRequestConnection *connection, id result, NSError *error) {
|
||||
_isCheckingSession = NO;
|
||||
if ([result isKindOfClass:[NSDictionary class]]) {
|
||||
_isCodelessIndexingEnabled = [((NSDictionary *)result)[CODELESS_INDEXING_STATUS_KEY] boolValue];
|
||||
if (_isCodelessIndexingEnabled) {
|
||||
_lastTreeHash = nil;
|
||||
if (!_appIndexingTimer) {
|
||||
_appIndexingTimer = [NSTimer timerWithTimeInterval:CODELESS_INDEXING_UPLOAD_INTERVAL_IN_SECONDS
|
||||
target:self
|
||||
selector:@selector(startIndexing)
|
||||
userInfo:nil
|
||||
repeats:YES];
|
||||
|
||||
[[NSRunLoop mainRunLoop] addTimer:_appIndexingTimer forMode:NSDefaultRunLoopMode];
|
||||
}
|
||||
} else {
|
||||
_deviceSessionID = nil;
|
||||
}
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
+ (NSString *)currentSessionDeviceID
|
||||
{
|
||||
if (!_deviceSessionID) {
|
||||
_deviceSessionID = [NSUUID UUID].UUIDString;
|
||||
}
|
||||
return _deviceSessionID;
|
||||
}
|
||||
|
||||
+ (NSString *)extInfo
|
||||
{
|
||||
struct utsname systemInfo;
|
||||
uname(&systemInfo);
|
||||
NSString *machine = @(systemInfo.machine);
|
||||
NSString *advertiserID = [FBSDKAppEventsUtility advertiserID] ?: @"";
|
||||
machine = machine ?: @"";
|
||||
NSString *debugStatus = [FBSDKAppEventsUtility isDebugBuild] ? @"1" : @"0";
|
||||
#if TARGET_IPHONE_SIMULATOR
|
||||
NSString *isSimulator = @"1";
|
||||
#else
|
||||
NSString *isSimulator = @"0";
|
||||
#endif
|
||||
NSLocale *locale = [NSLocale currentLocale];
|
||||
NSString *languageCode = [locale objectForKey:NSLocaleLanguageCode];
|
||||
NSString *countryCode = [locale objectForKey:NSLocaleCountryCode];
|
||||
NSString *localeString = locale.localeIdentifier;
|
||||
if (languageCode && countryCode) {
|
||||
localeString = [NSString stringWithFormat:@"%@_%@", languageCode, countryCode];
|
||||
}
|
||||
|
||||
NSString *extinfo = [FBSDKBasicUtility JSONStringForObject:@[machine,
|
||||
advertiserID,
|
||||
debugStatus,
|
||||
isSimulator,
|
||||
localeString]
|
||||
error:NULL
|
||||
invalidObjectHandler:NULL];
|
||||
|
||||
return extinfo ?: @"";
|
||||
}
|
||||
|
||||
+ (void)startIndexing {
|
||||
if (!_isCodelessIndexingEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (UIApplicationStateActive != [UIApplication sharedApplication].applicationState) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If userAgentSuffix begins with Unity, trigger unity code to upload view hierarchy
|
||||
NSString *userAgentSuffix = [FBSDKSettings userAgentSuffix];
|
||||
if (userAgentSuffix != nil && [userAgentSuffix hasPrefix:@"Unity"]) {
|
||||
Class FBUnityUtility = objc_lookUpClass("FBUnityUtility");
|
||||
SEL selector = NSSelectorFromString(@"triggerUploadViewHierarchy");
|
||||
if (FBUnityUtility && selector && [FBUnityUtility respondsToSelector:selector]) {
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
|
||||
[FBUnityUtility performSelector:selector];
|
||||
#pragma clang diagnostic pop
|
||||
}
|
||||
} else {
|
||||
[self uploadIndexing];
|
||||
}
|
||||
}
|
||||
|
||||
+ (void)uploadIndexing
|
||||
{
|
||||
if (_isCodelessIndexing) {
|
||||
return;
|
||||
}
|
||||
|
||||
NSString *tree = [FBSDKCodelessIndexer currentViewTree];
|
||||
|
||||
[self uploadIndexing:tree];
|
||||
}
|
||||
|
||||
+ (void)uploadIndexing:(NSString *)tree
|
||||
{
|
||||
if (_isCodelessIndexing) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!tree) {
|
||||
return;
|
||||
}
|
||||
|
||||
NSString *currentTreeHash = [FBSDKUtility SHA256Hash:tree];
|
||||
if (_lastTreeHash && [_lastTreeHash isEqualToString:currentTreeHash]) {
|
||||
return;
|
||||
}
|
||||
|
||||
_lastTreeHash = currentTreeHash;
|
||||
|
||||
NSBundle *mainBundle = [NSBundle mainBundle];
|
||||
NSString *version = [mainBundle objectForInfoDictionaryKey:@"CFBundleShortVersionString"];
|
||||
|
||||
FBSDKGraphRequest *request = [[FBSDKGraphRequest alloc]
|
||||
initWithGraphPath:[NSString stringWithFormat:@"%@/%@",
|
||||
[FBSDKSettings appID], CODELESS_INDEXING_ENDPOINT]
|
||||
parameters:@{
|
||||
CODELESS_INDEXING_TREE_KEY: tree,
|
||||
CODELESS_INDEXING_APP_VERSION_KEY: version ?: @"",
|
||||
CODELESS_INDEXING_PLATFORM_KEY: @"iOS",
|
||||
CODELESS_INDEXING_SESSION_ID_KEY: [self currentSessionDeviceID]
|
||||
}
|
||||
HTTPMethod:FBSDKHTTPMethodPOST];
|
||||
_isCodelessIndexing = YES;
|
||||
[request startWithCompletionHandler:^(FBSDKGraphRequestConnection *connection, id result, NSError *error) {
|
||||
_isCodelessIndexing = NO;
|
||||
if ([result isKindOfClass:[NSDictionary class]]) {
|
||||
_isCodelessIndexingEnabled = [result[CODELESS_INDEXING_STATUS_KEY] boolValue];
|
||||
if (!_isCodelessIndexingEnabled) {
|
||||
_deviceSessionID = nil;
|
||||
}
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
+ (NSString *)currentViewTree
|
||||
{
|
||||
NSMutableArray *trees = [NSMutableArray array];
|
||||
|
||||
NSArray *windows = [UIApplication sharedApplication].windows;
|
||||
for (UIWindow *window in windows) {
|
||||
NSDictionary *tree = [FBSDKCodelessIndexer recursiveCaptureTree:window];
|
||||
if (tree) {
|
||||
if (window.isKeyWindow) {
|
||||
[trees insertObject:tree atIndex:0];
|
||||
} else {
|
||||
[trees addObject:tree];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (0 == trees.count) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSArray *viewTrees = [trees reverseObjectEnumerator].allObjects;
|
||||
|
||||
NSData *data = UIImageJPEGRepresentation([FBSDKCodelessIndexer screenshot], 0.5);
|
||||
NSString *screenshot = [data base64EncodedStringWithOptions:0];
|
||||
|
||||
NSMutableDictionary *treeInfo = [NSMutableDictionary dictionary];
|
||||
|
||||
treeInfo[@"view"] = viewTrees;
|
||||
treeInfo[@"screenshot"] = screenshot ?: @"";
|
||||
|
||||
NSString *tree = nil;
|
||||
data = [NSJSONSerialization dataWithJSONObject:treeInfo options:0 error:nil];
|
||||
if (data) {
|
||||
tree = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
|
||||
}
|
||||
|
||||
return tree;
|
||||
}
|
||||
|
||||
+ (NSDictionary<NSString *, id> *)recursiveCaptureTree:(NSObject *)obj
|
||||
{
|
||||
if (!obj) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSMutableDictionary *result = [FBSDKViewHierarchy getDetailAttributesOf:obj];
|
||||
|
||||
NSArray *children = [FBSDKViewHierarchy getChildren:obj];
|
||||
NSMutableArray *childrenTrees = [NSMutableArray array];
|
||||
for (NSObject *child in children) {
|
||||
NSDictionary *objTree = [self recursiveCaptureTree:child];
|
||||
[childrenTrees addObject:objTree];
|
||||
}
|
||||
|
||||
if (childrenTrees.count > 0) {
|
||||
[result setValue:[childrenTrees copy] forKey:CODELESS_VIEW_TREE_CHILDREN_KEY];
|
||||
}
|
||||
|
||||
return [result copy];
|
||||
}
|
||||
|
||||
+ (UIImage *)screenshot {
|
||||
UIWindow *window = [UIApplication sharedApplication].delegate.window;
|
||||
|
||||
UIGraphicsBeginImageContext(window.bounds.size);
|
||||
[window drawViewHierarchyInRect:window.bounds afterScreenUpdates:YES];
|
||||
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
|
||||
UIGraphicsEndImageContext();
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
+ (NSDictionary<NSString *, NSNumber *> *)dimensionOf:(NSObject *)obj
|
||||
{
|
||||
UIView *view = nil;
|
||||
|
||||
if ([obj isKindOfClass:[UIView class]]) {
|
||||
view = (UIView *)obj;
|
||||
} else if ([obj isKindOfClass:[UIViewController class]]) {
|
||||
view = ((UIViewController *)obj).view;
|
||||
}
|
||||
|
||||
CGRect frame = view.frame;
|
||||
CGPoint offset = CGPointZero;
|
||||
|
||||
if ([view isKindOfClass:[UIScrollView class]])
|
||||
offset = ((UIScrollView *)view).contentOffset;
|
||||
|
||||
return @{
|
||||
CODELESS_VIEW_TREE_TOP_KEY: @((int)frame.origin.y),
|
||||
CODELESS_VIEW_TREE_LEFT_KEY: @((int)frame.origin.x),
|
||||
CODELESS_VIEW_TREE_WIDTH_KEY: @((int)frame.size.width),
|
||||
CODELESS_VIEW_TREE_HEIGHT_KEY: @((int)frame.size.height),
|
||||
CODELESS_VIEW_TREE_OFFSET_X_KEY: @((int)offset.x),
|
||||
CODELESS_VIEW_TREE_OFFSET_Y_KEY: @((int)offset.y),
|
||||
CODELESS_VIEW_TREE_VISIBILITY_KEY: view.isHidden ? @4 : @0
|
||||
};
|
||||
}
|
||||
|
||||
@end
|
||||
@ -0,0 +1,92 @@
|
||||
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
|
||||
//
|
||||
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
|
||||
// copy, modify, and distribute this software in source code or binary form for use
|
||||
// in connection with the web services and APIs provided by Facebook.
|
||||
//
|
||||
// As with any software that integrates with the Facebook platform, your use of
|
||||
// this software is subject to the Facebook Developer Principles and Policies
|
||||
// [http://developers.facebook.com/policy/]. This copyright 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.
|
||||
#ifndef FBSDKCodelessMacros_h
|
||||
#define FBSDKCodelessMacros_h
|
||||
|
||||
// keys for event binding path compoenent
|
||||
#define CODELESS_MAPPING_METHOD_KEY @"method"
|
||||
#define CODELESS_MAPPING_EVENT_NAME_KEY @"event_name"
|
||||
#define CODELESS_MAPPING_EVENT_TYPE_KEY @"event_type"
|
||||
#define CODELESS_MAPPING_APP_VERSION_KEY @"app_version"
|
||||
#define CODELESS_MAPPING_PATH_KEY @"path"
|
||||
#define CODELESS_MAPPING_PATH_TYPE_KEY @"path_type"
|
||||
#define CODELESS_MAPPING_CLASS_NAME_KEY @"class_name"
|
||||
#define CODELESS_MAPPING_MATCH_BITMASK_KEY @"match_bitmask"
|
||||
#define CODELESS_MAPPING_ID_KEY @"id"
|
||||
#define CODELESS_MAPPING_INDEX_KEY @"index"
|
||||
#define CODELESS_MAPPING_IS_USER_INPUT_KEY @"is_user_input"
|
||||
#define CODELESS_MAPPING_SECTION_KEY @"section"
|
||||
#define CODELESS_MAPPING_ROW_KEY @"row"
|
||||
#define CODELESS_MAPPING_TEXT_KEY @"text"
|
||||
#define CODELESS_MAPPING_TAG_KEY @"tag"
|
||||
#define CODELESS_MAPPING_DESC_KEY @"description"
|
||||
#define CODELESS_MAPPING_HINT_KEY @"hint"
|
||||
#define CODELESS_MAPPING_PARAMETERS_KEY @"parameters"
|
||||
#define CODELESS_MAPPING_PARAMETER_NAME_KEY @"name"
|
||||
#define CODELESS_MAPPING_PARAMETER_VALUE_KEY @"value"
|
||||
|
||||
#define CODELESS_MAPPING_PARENT_CLASS_NAME @".."
|
||||
#define CODELESS_MAPPING_CURRENT_CLASS_NAME @"."
|
||||
|
||||
#define ReactNativeClassRCTView "RCTView"
|
||||
#define ReactNativeClassRCTRootView "RCTRootView"
|
||||
|
||||
#define CODELESS_INDEXING_UPLOAD_INTERVAL_IN_SECONDS 1
|
||||
#define CODELESS_INDEXING_STATUS_KEY @"is_app_indexing_enabled"
|
||||
#define CODELESS_INDEXING_SESSION_ID_KEY @"device_session_id"
|
||||
#define CODELESS_INDEXING_APP_VERSION_KEY @"app_version"
|
||||
#define CODELESS_INDEXING_SDK_VERSION_KEY @"sdk_version"
|
||||
#define CODELESS_INDEXING_PLATFORM_KEY @"platform"
|
||||
#define CODELESS_INDEXING_TREE_KEY @"tree"
|
||||
#define CODELESS_INDEXING_SCREENSHOT_KEY @"screenshot"
|
||||
#define CODELESS_INDEXING_EXT_INFO_KEY @"extinfo"
|
||||
|
||||
#define CODELESS_INDEXING_ENDPOINT @"app_indexing"
|
||||
#define CODELESS_INDEXING_SESSION_ENDPOINT @"app_indexing_session"
|
||||
|
||||
#define CODELESS_SETUP_ENABLED_FIELD @"auto_event_setup_enabled"
|
||||
#define CODELESS_SETUP_ENABLED_KEY @"codeless_setup_enabled"
|
||||
#define CODELESS_SETTING_KEY @"com.facebook.sdk:codelessSetting%@"
|
||||
#define CODELESS_SETTING_TIMESTAMP_KEY @"codeless_setting_timestamp"
|
||||
#define CODELESS_SETTING_CACHE_TIMEOUT (7 * 24 * 60 * 60)
|
||||
|
||||
// keys for view tree
|
||||
#define CODELESS_VIEW_TREE_CLASS_NAME_KEY @"classname"
|
||||
#define CODELESS_VIEW_TREE_CLASS_TYPE_BIT_MASK_KEY @"classtypebitmask"
|
||||
#define CODELESS_VIEW_TREE_TEXT_KEY @"text"
|
||||
#define CODELESS_VIEW_TREE_DESC_KEY @"description"
|
||||
#define CODELESS_VIEW_TREE_DIMENSION_KEY @"dimension"
|
||||
#define CODELESS_VIEW_TREE_TAG_KEY @"tag"
|
||||
#define CODELESS_VIEW_TREE_CHILDREN_KEY @"childviews"
|
||||
#define CODELESS_VIEW_TREE_HINT_KEY @"hint"
|
||||
#define CODELESS_VIEW_TREE_ACTIONS_KEY @"actions"
|
||||
|
||||
#define CODELESS_VIEW_TREE_TOP_KEY @"top"
|
||||
#define CODELESS_VIEW_TREE_LEFT_KEY @"left"
|
||||
#define CODELESS_VIEW_TREE_WIDTH_KEY @"width"
|
||||
#define CODELESS_VIEW_TREE_HEIGHT_KEY @"height"
|
||||
#define CODELESS_VIEW_TREE_OFFSET_X_KEY @"scrollx"
|
||||
#define CODELESS_VIEW_TREE_OFFSET_Y_KEY @"scrolly"
|
||||
#define CODELESS_VIEW_TREE_VISIBILITY_KEY @"visibility"
|
||||
|
||||
#define CODELESS_VIEW_TREE_TEXT_STYLE_KEY @"text_style"
|
||||
#define CODELESS_VIEW_TREE_TEXT_IS_BOLD_KEY @"is_bold"
|
||||
#define CODELESS_VIEW_TREE_TEXT_IS_ITALIC_KEY @"is_italic"
|
||||
#define CODELESS_VIEW_TREE_TEXT_SIZE_KEY @"font_size"
|
||||
|
||||
#endif /* FBSDKCodelessMacros_h */
|
||||
@ -0,0 +1,31 @@
|
||||
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
|
||||
//
|
||||
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
|
||||
// copy, modify, and distribute this software in source code or binary form for use
|
||||
// in connection with the web services and APIs provided by Facebook.
|
||||
//
|
||||
// As with any software that integrates with the Facebook platform, your use of
|
||||
// this software is subject to the Facebook Developer Principles and Policies
|
||||
// [http://developers.facebook.com/policy/]. This copyright 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 <Foundation/Foundation.h>
|
||||
|
||||
NS_SWIFT_NAME(CodelessParameterComponent)
|
||||
@interface FBSDKCodelessParameterComponent : NSObject
|
||||
|
||||
@property (nonatomic, copy, readonly) NSString *name;
|
||||
@property (nonatomic, copy, readonly) NSString *value;
|
||||
@property (nonatomic, readonly) NSArray *path;
|
||||
@property (nonatomic, copy, readonly) NSString *pathType;
|
||||
|
||||
- (instancetype)initWithJSON:(NSDictionary *)dict;
|
||||
|
||||
@end
|
||||
@ -0,0 +1,44 @@
|
||||
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
|
||||
//
|
||||
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
|
||||
// copy, modify, and distribute this software in source code or binary form for use
|
||||
// in connection with the web services and APIs provided by Facebook.
|
||||
//
|
||||
// As with any software that integrates with the Facebook platform, your use of
|
||||
// this software is subject to the Facebook Developer Principles and Policies
|
||||
// [http://developers.facebook.com/policy/]. This copyright 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 "FBSDKCodelessParameterComponent.h"
|
||||
|
||||
#import "FBSDKCodelessMacros.h"
|
||||
#import "FBSDKCodelessPathComponent.h"
|
||||
|
||||
@implementation FBSDKCodelessParameterComponent
|
||||
|
||||
- (instancetype)initWithJSON:(NSDictionary *)dict {
|
||||
if (self = [super init]) {
|
||||
_name = [dict[CODELESS_MAPPING_PARAMETER_NAME_KEY] copy];
|
||||
_value = [dict[CODELESS_MAPPING_PARAMETER_VALUE_KEY] copy];
|
||||
_pathType = [dict[CODELESS_MAPPING_PATH_TYPE_KEY] copy];
|
||||
|
||||
NSArray *ary = dict[CODELESS_MAPPING_PATH_KEY];
|
||||
NSMutableArray *mut = [NSMutableArray array];
|
||||
for (NSDictionary *info in ary) {
|
||||
FBSDKCodelessPathComponent *component = [[FBSDKCodelessPathComponent alloc] initWithJSON:info];
|
||||
[mut addObject:component];
|
||||
}
|
||||
_path = [mut copy];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
@ -0,0 +1,45 @@
|
||||
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
|
||||
//
|
||||
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
|
||||
// copy, modify, and distribute this software in source code or binary form for use
|
||||
// in connection with the web services and APIs provided by Facebook.
|
||||
//
|
||||
// As with any software that integrates with the Facebook platform, your use of
|
||||
// this software is subject to the Facebook Developer Principles and Policies
|
||||
// [http://developers.facebook.com/policy/]. This copyright 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 <Foundation/Foundation.h>
|
||||
|
||||
typedef NS_OPTIONS(int, FBSDKCodelessMatchBitmaskField)
|
||||
{
|
||||
FBSDKCodelessMatchBitmaskFieldID = 1,
|
||||
FBSDKCodelessMatchBitmaskFieldText = 1 << 1,
|
||||
FBSDKCodelessMatchBitmaskFieldTag = 1 << 2,
|
||||
FBSDKCodelessMatchBitmaskFieldDescription = 1 << 3,
|
||||
FBSDKCodelessMatchBitmaskFieldHint = 1 << 4
|
||||
};
|
||||
|
||||
NS_SWIFT_NAME(CodelessPathComponent)
|
||||
@interface FBSDKCodelessPathComponent : NSObject
|
||||
|
||||
@property (nonatomic, copy, readonly) NSString *className;
|
||||
@property (nonatomic, copy, readonly) NSString *text;
|
||||
@property (nonatomic, copy, readonly) NSString *hint;
|
||||
@property (nonatomic, copy, readonly) NSString *desc; // description
|
||||
@property (nonatomic, readonly) int index;
|
||||
@property (nonatomic, readonly) int tag;
|
||||
@property (nonatomic, readonly) int section;
|
||||
@property (nonatomic, readonly) int row;
|
||||
@property (nonatomic, readonly) int matchBitmask;
|
||||
|
||||
- (instancetype)initWithJSON:(NSDictionary*)dict;
|
||||
|
||||
@end
|
||||
@ -0,0 +1,58 @@
|
||||
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
|
||||
//
|
||||
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
|
||||
// copy, modify, and distribute this software in source code or binary form for use
|
||||
// in connection with the web services and APIs provided by Facebook.
|
||||
//
|
||||
// As with any software that integrates with the Facebook platform, your use of
|
||||
// this software is subject to the Facebook Developer Principles and Policies
|
||||
// [http://developers.facebook.com/policy/]. This copyright 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 "FBSDKCodelessPathComponent.h"
|
||||
|
||||
#import "FBSDKCodelessMacros.h"
|
||||
|
||||
@implementation FBSDKCodelessPathComponent
|
||||
|
||||
- (instancetype)initWithJSON:(NSDictionary *)dict {
|
||||
if (self = [super init]) {
|
||||
_className = [dict[CODELESS_MAPPING_CLASS_NAME_KEY] copy];
|
||||
_text = [dict[CODELESS_MAPPING_TEXT_KEY] copy];
|
||||
_hint = [dict[CODELESS_MAPPING_HINT_KEY] copy];
|
||||
_desc = [dict[CODELESS_MAPPING_DESC_KEY] copy];
|
||||
|
||||
|
||||
if (dict[CODELESS_MAPPING_INDEX_KEY]) {
|
||||
_index = [dict[CODELESS_MAPPING_INDEX_KEY] intValue];
|
||||
} else {
|
||||
_index = -1;
|
||||
}
|
||||
|
||||
if (dict[CODELESS_MAPPING_SECTION_KEY]) {
|
||||
_section = [dict[CODELESS_MAPPING_SECTION_KEY] intValue];
|
||||
} else {
|
||||
_section = -1;
|
||||
}
|
||||
|
||||
if (dict[CODELESS_MAPPING_ROW_KEY]) {
|
||||
_row = [dict[CODELESS_MAPPING_ROW_KEY] intValue];
|
||||
} else {
|
||||
_row = -1;
|
||||
}
|
||||
|
||||
_tag = [dict[CODELESS_MAPPING_TAG_KEY] intValue];
|
||||
_matchBitmask = [dict[CODELESS_MAPPING_MATCH_BITMASK_KEY] intValue];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
38
Example/Pods/FBSDKCoreKit/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/Codeless/FBSDKEventBinding.h
generated
Normal file
38
Example/Pods/FBSDKCoreKit/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/Codeless/FBSDKEventBinding.h
generated
Normal file
@ -0,0 +1,38 @@
|
||||
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
|
||||
//
|
||||
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
|
||||
// copy, modify, and distribute this software in source code or binary form for use
|
||||
// in connection with the web services and APIs provided by Facebook.
|
||||
//
|
||||
// As with any software that integrates with the Facebook platform, your use of
|
||||
// this software is subject to the Facebook Developer Principles and Policies
|
||||
// [http://developers.facebook.com/policy/]. This copyright 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 <Foundation/Foundation.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_SWIFT_NAME(EventBinding)
|
||||
@interface FBSDKEventBinding : NSObject
|
||||
|
||||
@property (nonatomic, copy, readonly) NSString *eventName;
|
||||
@property (nonatomic, copy, readonly) NSString *eventType;
|
||||
@property (nonatomic, copy, readonly) NSString *appVersion;
|
||||
@property (nonatomic, readonly) NSArray *path;
|
||||
@property (nonatomic, copy, readonly) NSString *pathType;
|
||||
@property (nonatomic, readonly) NSArray *parameters;
|
||||
|
||||
+ (BOOL)isViewMatchPath:(UIView *)view path:(NSArray *)path;
|
||||
+ (BOOL)isPath:(NSArray *)path matchViewPath:(NSArray *)viewPath;
|
||||
- (FBSDKEventBinding *)initWithJSON:(NSDictionary *)dict;
|
||||
- (void)trackEvent:(id)sender;
|
||||
|
||||
@end
|
||||
277
Example/Pods/FBSDKCoreKit/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/Codeless/FBSDKEventBinding.m
generated
Normal file
277
Example/Pods/FBSDKCoreKit/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/Codeless/FBSDKEventBinding.m
generated
Normal file
@ -0,0 +1,277 @@
|
||||
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
|
||||
//
|
||||
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
|
||||
// copy, modify, and distribute this software in source code or binary form for use
|
||||
// in connection with the web services and APIs provided by Facebook.
|
||||
//
|
||||
// As with any software that integrates with the Facebook platform, your use of
|
||||
// this software is subject to the Facebook Developer Principles and Policies
|
||||
// [http://developers.facebook.com/policy/]. This copyright 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 "FBSDKEventBinding.h"
|
||||
|
||||
#import <FBSDKCoreKit/FBSDKAppEvents.h>
|
||||
#import <FBSDKCoreKit/FBSDKUtility.h>
|
||||
|
||||
#import "FBSDKAppEventsUtility.h"
|
||||
#import "FBSDKCodelessMacros.h"
|
||||
#import "FBSDKCodelessParameterComponent.h"
|
||||
#import "FBSDKCodelessPathComponent.h"
|
||||
#import "FBSDKSwizzler.h"
|
||||
#import "FBSDKViewHierarchy.h"
|
||||
|
||||
#define CODELESS_PATH_TYPE_ABSOLUTE @"absolute"
|
||||
#define CODELESS_PATH_TYPE_RELATIVE @"relative"
|
||||
#define CODELESS_CODELESS_EVENT_KEY @"_is_fb_codeless"
|
||||
#define PARAMETER_NAME_PRICE @"_valueToSum"
|
||||
|
||||
@implementation FBSDKEventBinding
|
||||
|
||||
- (FBSDKEventBinding *)initWithJSON:(NSDictionary *)dict
|
||||
{
|
||||
if ((self = [super init])) {
|
||||
_eventName = [dict[CODELESS_MAPPING_EVENT_NAME_KEY] copy];
|
||||
_eventType = [dict[CODELESS_MAPPING_EVENT_TYPE_KEY] copy];
|
||||
_appVersion = [dict[CODELESS_MAPPING_APP_VERSION_KEY] copy];
|
||||
_pathType = [dict[CODELESS_MAPPING_PATH_TYPE_KEY] copy];
|
||||
|
||||
NSArray *pathComponents = dict[CODELESS_MAPPING_PATH_KEY];
|
||||
NSMutableArray *mut = [NSMutableArray array];
|
||||
for (NSDictionary *info in pathComponents) {
|
||||
FBSDKCodelessPathComponent *component = [[FBSDKCodelessPathComponent alloc] initWithJSON:info];
|
||||
[mut addObject:component];
|
||||
}
|
||||
_path = [mut copy];
|
||||
|
||||
NSArray *parameters = dict[CODELESS_MAPPING_PARAMETERS_KEY];
|
||||
mut = [NSMutableArray array];
|
||||
for (NSDictionary *info in parameters) {
|
||||
FBSDKCodelessParameterComponent *component = [[FBSDKCodelessParameterComponent alloc] initWithJSON:info];
|
||||
[mut addObject:component];
|
||||
}
|
||||
_parameters = [mut copy];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)trackEvent:(id)sender
|
||||
{
|
||||
UIView *sourceView = [sender isKindOfClass:[UIView class]] ? (UIView *)sender : nil;
|
||||
NSMutableDictionary *params = [NSMutableDictionary dictionary];
|
||||
params[CODELESS_CODELESS_EVENT_KEY] = @"1";
|
||||
for (FBSDKCodelessParameterComponent *component in self.parameters) {
|
||||
NSString *text = component.value;
|
||||
if (!text || text.length == 0) {
|
||||
text = [FBSDKEventBinding findParameterOfPath:component.path
|
||||
pathType:component.pathType
|
||||
sourceView:sourceView];
|
||||
}
|
||||
if (text) {
|
||||
if ([component.name isEqualToString:PARAMETER_NAME_PRICE]) {
|
||||
NSNumber *value = [FBSDKAppEventsUtility getNumberValue:text];
|
||||
params[component.name] = value;
|
||||
} else {
|
||||
params[component.name] = text;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[FBSDKAppEvents logEvent:_eventName parameters:[params copy]];
|
||||
}
|
||||
|
||||
+ (BOOL)matchAnyView:(NSArray *)views
|
||||
pathComponent:(FBSDKCodelessPathComponent *)component
|
||||
{
|
||||
for (NSObject *view in views) {
|
||||
if ([self match:view pathComponent:component]) {
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
+ (BOOL)match:(NSObject *)view
|
||||
pathComponent:(FBSDKCodelessPathComponent *)component
|
||||
{
|
||||
NSString *className = NSStringFromClass([view class]);
|
||||
if (![className isEqualToString:component.className]) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
if (component.index >= 0) {
|
||||
NSObject *parent = [FBSDKViewHierarchy getParent:view];
|
||||
if (parent) {
|
||||
NSArray *children = [FBSDKViewHierarchy getChildren:[FBSDKViewHierarchy getParent:view]];
|
||||
NSUInteger index = [children indexOfObject:view];
|
||||
if (index == NSNotFound || index != component.index) {
|
||||
return NO;
|
||||
}
|
||||
} else {
|
||||
if (0 != component.index) {
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((component.matchBitmask & FBSDKCodelessMatchBitmaskFieldText) > 0) {
|
||||
NSString *text = [FBSDKViewHierarchy getText:view];
|
||||
BOOL match = ((text.length == 0 && component.text.length == 0)
|
||||
|| [text isEqualToString:component.text]);
|
||||
if (!match) {
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
if ((component.matchBitmask & FBSDKCodelessMatchBitmaskFieldTag) > 0
|
||||
&& [view isKindOfClass:[UIView class]]
|
||||
&& component.tag != ((UIView *)view).tag) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
if ((component.matchBitmask & FBSDKCodelessMatchBitmaskFieldHint) > 0) {
|
||||
NSString *hint = [FBSDKViewHierarchy getHint:view];
|
||||
BOOL match = ((hint.length == 0 && component.hint.length == 0)
|
||||
|| [hint isEqualToString:component.hint]);
|
||||
if (!match) {
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
+ (BOOL)isViewMatchPath:(UIView *)view path:(NSArray *)path
|
||||
{
|
||||
NSArray *viewPath = [FBSDKViewHierarchy getPath:view];
|
||||
BOOL isMatch = [self isPath:path matchViewPath:viewPath];
|
||||
|
||||
return isMatch;
|
||||
}
|
||||
|
||||
+ (BOOL)isPath:(NSArray *)path matchViewPath:(NSArray *)viewPath {
|
||||
for (NSInteger i = 0; i < MIN(path.count, viewPath.count); i++) {
|
||||
NSInteger idxPath = path.count - i - 1;
|
||||
NSInteger idxViewPath = viewPath.count - i - 1;
|
||||
|
||||
FBSDKCodelessPathComponent *pathComponent = path[idxPath];
|
||||
FBSDKCodelessPathComponent *viewPathComponent = viewPath[idxViewPath];
|
||||
|
||||
if (![pathComponent.className isEqualToString:viewPathComponent.className]) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
if (pathComponent.index >= 0
|
||||
&& pathComponent.index != viewPathComponent.index) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
if ((pathComponent.matchBitmask & FBSDKCodelessMatchBitmaskFieldText) > 0) {
|
||||
NSString *text = viewPathComponent.text;
|
||||
BOOL match = ((text.length == 0 && pathComponent.text.length == 0)
|
||||
|| [text isEqualToString:pathComponent.text]
|
||||
|| [[FBSDKUtility SHA256Hash:text] isEqualToString:pathComponent.text]);
|
||||
if (!match) {
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
if ((pathComponent.matchBitmask & FBSDKCodelessMatchBitmaskFieldTag) > 0
|
||||
&& pathComponent.tag != viewPathComponent.tag) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
if ((pathComponent.matchBitmask & FBSDKCodelessMatchBitmaskFieldHint) > 0) {
|
||||
NSString *hint = viewPathComponent.hint;
|
||||
BOOL match = ((hint.length == 0 && pathComponent.hint.length == 0)
|
||||
|| [hint isEqualToString:pathComponent.hint]
|
||||
|| [[FBSDKUtility SHA256Hash:hint] isEqualToString:pathComponent.hint]);
|
||||
if (!match) {
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
+ (NSObject *)findViewByPath:(NSArray *)path parent:(NSObject *)parent level:(int)level {
|
||||
if (level >= path.count) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
FBSDKCodelessPathComponent *pathComponent = path[level];
|
||||
|
||||
// If found parent, skip to next level
|
||||
if ([pathComponent.className isEqualToString:CODELESS_MAPPING_PARENT_CLASS_NAME]) {
|
||||
NSObject *nextParent = [FBSDKViewHierarchy getParent:parent];
|
||||
|
||||
return [FBSDKEventBinding findViewByPath:path parent:nextParent level:level + 1];
|
||||
} else if ([pathComponent.className isEqualToString:CODELESS_MAPPING_CURRENT_CLASS_NAME]) {
|
||||
return parent;
|
||||
}
|
||||
|
||||
NSArray *children;
|
||||
if (parent) {
|
||||
children = [FBSDKViewHierarchy getChildren:parent];
|
||||
} else {
|
||||
UIWindow *window = [UIApplication sharedApplication].delegate.window;
|
||||
if (window) {
|
||||
children = @[window];
|
||||
} else {
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
if (path.count - 1 == level) {
|
||||
int index = pathComponent.index;
|
||||
if (index >= 0) {
|
||||
NSObject *child = index < children.count ? children[index] : nil;
|
||||
if ([self match:child pathComponent:pathComponent]) {
|
||||
return child;
|
||||
}
|
||||
} else {
|
||||
for (NSObject *child in children) {
|
||||
if ([self match:child pathComponent:pathComponent]) {
|
||||
return child;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (NSObject *child in children) {
|
||||
NSObject *result = [self findViewByPath:path parent:child level:level + 1];
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
// MARK: - find event parameters via relative path
|
||||
+ (NSString *)findParameterOfPath:(NSArray *)path
|
||||
pathType:(NSString *)pathType
|
||||
sourceView:(UIView *)sourceView {
|
||||
if (0 == path.count) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
UIView *rootView = sourceView;
|
||||
if (![pathType isEqualToString:CODELESS_PATH_TYPE_RELATIVE]) {
|
||||
rootView = nil;
|
||||
}
|
||||
|
||||
NSObject *foundObj = [self findViewByPath:path parent:rootView level:0];
|
||||
|
||||
return [FBSDKViewHierarchy getText:foundObj];
|
||||
}
|
||||
|
||||
@end
|
||||
@ -0,0 +1,28 @@
|
||||
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
|
||||
//
|
||||
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
|
||||
// copy, modify, and distribute this software in source code or binary form for use
|
||||
// in connection with the web services and APIs provided by Facebook.
|
||||
//
|
||||
// As with any software that integrates with the Facebook platform, your use of
|
||||
// this software is subject to the Facebook Developer Principles and Policies
|
||||
// [http://developers.facebook.com/policy/]. This copyright 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 <Foundation/Foundation.h>
|
||||
|
||||
NS_SWIFT_NAME(EventBindingManager)
|
||||
@interface FBSDKEventBindingManager : NSObject
|
||||
|
||||
- (FBSDKEventBindingManager*)initWithJSON:(NSDictionary*)dict;
|
||||
- (void)updateBindings:(NSArray *)bindings;
|
||||
+ (NSArray *)parseArray:(NSArray *)array;
|
||||
|
||||
@end
|
||||
@ -0,0 +1,403 @@
|
||||
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
|
||||
//
|
||||
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
|
||||
// copy, modify, and distribute this software in source code or binary form for use
|
||||
// in connection with the web services and APIs provided by Facebook.
|
||||
//
|
||||
// As with any software that integrates with the Facebook platform, your use of
|
||||
// this software is subject to the Facebook Developer Principles and Policies
|
||||
// [http://developers.facebook.com/policy/]. This copyright 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 "FBSDKEventBindingManager.h"
|
||||
|
||||
#import <objc/runtime.h>
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import "FBSDKCodelessMacros.h"
|
||||
#import "FBSDKCodelessPathComponent.h"
|
||||
#import "FBSDKEventBinding.h"
|
||||
#import "FBSDKSwizzler.h"
|
||||
#import "FBSDKTypeUtility.h"
|
||||
#import "FBSDKViewHierarchy.h"
|
||||
|
||||
#define ReactNativeTargetKey @"target"
|
||||
#define ReactNativeTouchEndEventName @"touchEnd"
|
||||
|
||||
#define ReactNativeClassRCTTextView "RCTTextView"
|
||||
#define ReactNativeClassRCTImageView "RCTImageVIew"
|
||||
#define ReactNativeClassRCTTouchEvent "RCTTouchEvent"
|
||||
#define ReactNativeClassRCTTouchHandler "RCTTouchHandler"
|
||||
|
||||
static void fb_dispatch_on_main_thread(dispatch_block_t block) {
|
||||
if ([NSThread isMainThread]) {
|
||||
block();
|
||||
} else {
|
||||
dispatch_async(dispatch_get_main_queue(), block);
|
||||
}
|
||||
}
|
||||
|
||||
static void fb_dispatch_on_default_thread(dispatch_block_t block) {
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), block);
|
||||
}
|
||||
|
||||
@interface FBSDKEventBindingManager ()
|
||||
{
|
||||
BOOL isStarted;
|
||||
NSMutableDictionary *reactBindings;
|
||||
NSSet *validClasses;
|
||||
BOOL hasReactNative;
|
||||
NSArray *eventBindings;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation FBSDKEventBindingManager
|
||||
|
||||
- (id)init {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
isStarted = NO;
|
||||
hasReactNative = NO;
|
||||
reactBindings = [NSMutableDictionary dictionary];
|
||||
|
||||
NSMutableSet *classes = [NSMutableSet set];
|
||||
[classes addObject:[UIControl class]];
|
||||
[classes addObject:[UITableView class]];
|
||||
[classes addObject:[UICollectionView class]];
|
||||
// ReactNative
|
||||
Class classRCTRootView = objc_lookUpClass(ReactNativeClassRCTRootView);
|
||||
if (classRCTRootView != nil) {
|
||||
hasReactNative = YES;
|
||||
Class classRCTView = objc_lookUpClass(ReactNativeClassRCTView);
|
||||
Class classRCTTextView = objc_lookUpClass(ReactNativeClassRCTTextView);
|
||||
Class classRCTImageView = objc_lookUpClass(ReactNativeClassRCTImageView);
|
||||
if (classRCTView) {
|
||||
[classes addObject:classRCTView];
|
||||
}
|
||||
if (classRCTTextView) {
|
||||
[classes addObject:classRCTTextView];
|
||||
}
|
||||
if (classRCTImageView) {
|
||||
[classes addObject:classRCTImageView];
|
||||
}
|
||||
}
|
||||
validClasses = [NSSet setWithSet:classes];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
+ (NSArray *)parseArray:(NSArray *)array {
|
||||
NSMutableArray *result = [NSMutableArray array];
|
||||
|
||||
for (NSDictionary *json in array) {
|
||||
FBSDKEventBinding *binding = [[FBSDKEventBinding alloc] initWithJSON:json];
|
||||
[result addObject:binding];
|
||||
}
|
||||
|
||||
return [result copy];
|
||||
}
|
||||
|
||||
- (FBSDKEventBindingManager*)initWithJSON:(NSDictionary*)dict
|
||||
{
|
||||
if ((self = [super init])) {
|
||||
NSArray *eventBindingsDict = [FBSDKTypeUtility arrayValue:dict[@"event_bindings"]];
|
||||
NSMutableArray *bindings = [NSMutableArray array];
|
||||
for (NSDictionary *d in eventBindingsDict) {
|
||||
FBSDKEventBinding *e = [[FBSDKEventBinding alloc] initWithJSON:d];
|
||||
[bindings addObject:e];
|
||||
}
|
||||
eventBindings = [bindings copy];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wundeclared-selector"
|
||||
- (void)start
|
||||
{
|
||||
if (isStarted) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (0 == eventBindings.count) {
|
||||
return;
|
||||
}
|
||||
|
||||
isStarted = YES;
|
||||
|
||||
void (^blockToWindow)(id view) = ^(id view) {
|
||||
[self matchView:view delegate:nil];
|
||||
};
|
||||
|
||||
[FBSDKSwizzler swizzleSelector:@selector(didMoveToWindow)
|
||||
onClass:[UIControl class]
|
||||
withBlock:blockToWindow named:@"map_control"];
|
||||
|
||||
// ReactNative
|
||||
if (hasReactNative) { // If app is built via ReactNative
|
||||
Class classRCTView = objc_lookUpClass(ReactNativeClassRCTView);
|
||||
Class classRCTTextView = objc_lookUpClass(ReactNativeClassRCTTextView);
|
||||
Class classRCTImageView = objc_lookUpClass(ReactNativeClassRCTImageView);
|
||||
Class classRCTTouchHandler = objc_lookUpClass(ReactNativeClassRCTTouchHandler);
|
||||
|
||||
// All react-native views would be added tp RCTRootView, so no need to check didMoveToWindow
|
||||
[FBSDKSwizzler swizzleSelector:@selector(didMoveToWindow)
|
||||
onClass:classRCTView
|
||||
withBlock:blockToWindow
|
||||
named:@"match_react_native"];
|
||||
[FBSDKSwizzler swizzleSelector:@selector(didMoveToWindow)
|
||||
onClass:classRCTTextView
|
||||
withBlock:blockToWindow
|
||||
named:@"match_react_native"];
|
||||
[FBSDKSwizzler swizzleSelector:@selector(didMoveToWindow)
|
||||
onClass:classRCTImageView
|
||||
withBlock:blockToWindow
|
||||
named:@"match_react_native"];
|
||||
|
||||
// RCTTouchHandler handles with touch events, like touchEnd and uses RCTEventDispather to dispatch events, so we can check _updateAndDispatchTouches to fire events
|
||||
[FBSDKSwizzler swizzleSelector:@selector(_updateAndDispatchTouches:eventName:) onClass:classRCTTouchHandler withBlock:^(id touchHandler, SEL command, id touches, id eventName){
|
||||
if ([touches isKindOfClass:[NSSet class]] && [eventName isKindOfClass:[NSString class]]) {
|
||||
@try {
|
||||
NSString *reactEventName = (NSString *)eventName;
|
||||
NSSet<UITouch *> *reactTouches = (NSSet<UITouch *> *)touches;
|
||||
if ([reactEventName isEqualToString:ReactNativeTouchEndEventName]) {
|
||||
for (UITouch *touch in reactTouches) {
|
||||
UIView *targetView = ((UITouch *)touch).view.superview;
|
||||
NSNumber *reactTag = nil;
|
||||
// Find the closest React-managed touchable view like RCTTouchHandler
|
||||
while(targetView) {
|
||||
reactTag = [FBSDKViewHierarchy getViewReactTag:targetView];
|
||||
if (reactTag != nil && targetView.userInteractionEnabled) {
|
||||
break;
|
||||
}
|
||||
targetView = targetView.superview;
|
||||
}
|
||||
FBSDKEventBinding *eventBinding = self->reactBindings[reactTag];
|
||||
if (reactTag != nil && eventBinding != nil) {
|
||||
[eventBinding trackEvent:nil];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@catch(NSException *exception) {
|
||||
// Catch exception here to prevent LytroKit from crashing app
|
||||
}
|
||||
}
|
||||
} named:@"dispatch_rn_event"];
|
||||
}
|
||||
|
||||
// UITableView
|
||||
void (^tableViewBlock)(UITableView *tableView,
|
||||
SEL cmd,
|
||||
id<UITableViewDelegate> delegate) =
|
||||
^(UITableView *tableView, SEL cmd, id<UITableViewDelegate> delegate) {
|
||||
if (!delegate) {
|
||||
return;
|
||||
}
|
||||
|
||||
[self matchView:tableView delegate:delegate];
|
||||
};
|
||||
[FBSDKSwizzler swizzleSelector:@selector(setDelegate:)
|
||||
onClass:[UITableView class]
|
||||
withBlock:tableViewBlock
|
||||
named:@"match_table_view"];
|
||||
// UICollectionView
|
||||
void (^collectionViewBlock)(UICollectionView *collectionView,
|
||||
SEL cmd,
|
||||
id<UICollectionViewDelegate> delegate) =
|
||||
^(UICollectionView *collectionView, SEL cmd, id<UICollectionViewDelegate> delegate) {
|
||||
if (nil == delegate) {
|
||||
return;
|
||||
}
|
||||
|
||||
[self matchView:collectionView delegate:delegate];
|
||||
};
|
||||
[FBSDKSwizzler swizzleSelector:@selector(setDelegate:)
|
||||
onClass:[UICollectionView class]
|
||||
withBlock:collectionViewBlock
|
||||
named:@"handle_collection_view"];
|
||||
}
|
||||
|
||||
- (void)rematchBindings {
|
||||
if (0 == eventBindings.count) {
|
||||
return;
|
||||
}
|
||||
|
||||
NSArray *windows = [UIApplication sharedApplication].windows;
|
||||
for (UIWindow *window in windows) {
|
||||
[self matchSubviewsIn:window];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)matchSubviewsIn:(UIView *)view {
|
||||
if (!view) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (UIView *subview in view.subviews) {
|
||||
BOOL isValidClass = NO;
|
||||
for (Class cls in validClasses) {
|
||||
if ([subview isKindOfClass:cls]) {
|
||||
isValidClass = YES;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (isValidClass) {
|
||||
if ([subview isKindOfClass:[UITableView class]]) {
|
||||
UITableView *tableView = (UITableView *)subview;
|
||||
if (tableView.delegate) {
|
||||
[self matchView:subview delegate:tableView.delegate];
|
||||
}
|
||||
} else if ([subview isKindOfClass:[UICollectionView class]]) {
|
||||
UICollectionView *collectionView = (UICollectionView *)subview;
|
||||
if (collectionView.delegate) {
|
||||
[self matchView:subview delegate:collectionView.delegate];
|
||||
}
|
||||
} else {
|
||||
[self matchView:subview delegate:nil];
|
||||
}
|
||||
}
|
||||
|
||||
if (![subview isKindOfClass:[UIControl class]]) {
|
||||
[self matchSubviewsIn:subview];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check if the view is matched to any event
|
||||
- (void)matchView:(UIView *)view delegate:(id)delegate {
|
||||
if (0 == eventBindings.count) {
|
||||
return;
|
||||
}
|
||||
|
||||
fb_dispatch_on_main_thread(^{
|
||||
if (![view window]) {
|
||||
return;
|
||||
}
|
||||
|
||||
NSArray *path = [FBSDKViewHierarchy getPath:view];
|
||||
|
||||
fb_dispatch_on_default_thread(^{
|
||||
if ([view isKindOfClass:[UIControl class]]) {
|
||||
UIControl *control = (UIControl *)view;
|
||||
for (FBSDKEventBinding *binding in self->eventBindings) {
|
||||
if ([FBSDKEventBinding isPath:binding.path matchViewPath:path]) {
|
||||
fb_dispatch_on_main_thread(^{
|
||||
[control addTarget:binding
|
||||
action:@selector(trackEvent:)
|
||||
forControlEvents:UIControlEventTouchUpInside];
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (self->hasReactNative
|
||||
&& [view respondsToSelector:@selector(reactTag)]) {
|
||||
for (FBSDKEventBinding *binding in self->eventBindings) {
|
||||
if ([FBSDKEventBinding isPath:binding.path matchViewPath:path]) {
|
||||
fb_dispatch_on_main_thread(^{
|
||||
if (view) {
|
||||
NSNumber *reactTag = [FBSDKViewHierarchy getViewReactTag:view];
|
||||
if (reactTag != nil) {
|
||||
self->reactBindings[reactTag] = binding;
|
||||
}
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if ([view isKindOfClass:[UITableView class]]
|
||||
&& [delegate conformsToProtocol:@protocol(UITableViewDelegate)]) {
|
||||
fb_dispatch_on_default_thread(^{
|
||||
NSMutableSet *matchedBindings = [NSMutableSet set];
|
||||
for (FBSDKEventBinding *binding in self->eventBindings) {
|
||||
if (binding.path.count > 1) {
|
||||
NSArray *shortPath = [binding.path
|
||||
subarrayWithRange:NSMakeRange(0, binding.path.count - 1)];
|
||||
if ([FBSDKEventBinding isPath:shortPath matchViewPath:path]) {
|
||||
[matchedBindings addObject:binding];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (matchedBindings.count > 0) {
|
||||
NSArray *bindings = matchedBindings.allObjects;
|
||||
void (^block)(id, SEL, id, id) = ^(id target, SEL command, UITableView *tableView, NSIndexPath *indexPath) {
|
||||
fb_dispatch_on_main_thread(^{
|
||||
for (FBSDKEventBinding *binding in bindings) {
|
||||
FBSDKCodelessPathComponent *component = binding.path.lastObject;
|
||||
if ((component.section == -1 || component.section == indexPath.section)
|
||||
&& (component.row == -1 || component.row == indexPath.row)) {
|
||||
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
|
||||
[binding trackEvent:cell];
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
[FBSDKSwizzler swizzleSelector:@selector(tableView:didSelectRowAtIndexPath:)
|
||||
onClass:[delegate class]
|
||||
withBlock:block
|
||||
named:@"handle_table_view"];
|
||||
}
|
||||
});
|
||||
} else if ([view isKindOfClass:[UICollectionView class]]
|
||||
&& [delegate conformsToProtocol:@protocol(UICollectionViewDelegate)]) {
|
||||
fb_dispatch_on_default_thread(^{
|
||||
NSMutableSet *matchedBindings = [NSMutableSet set];
|
||||
for (FBSDKEventBinding *binding in self->eventBindings) {
|
||||
if (binding.path.count > 1) {
|
||||
NSArray *shortPath = [binding.path
|
||||
subarrayWithRange:NSMakeRange(0, binding.path.count - 1)];
|
||||
if ([FBSDKEventBinding isPath:shortPath matchViewPath:path]) {
|
||||
[matchedBindings addObject:binding];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (matchedBindings.count > 0) {
|
||||
NSArray *bindings = matchedBindings.allObjects;
|
||||
void (^block)(id, SEL, id, id) = ^(id target, SEL command, UICollectionView *collectionView, NSIndexPath *indexPath) {
|
||||
fb_dispatch_on_main_thread(^{
|
||||
for (FBSDKEventBinding *binding in bindings) {
|
||||
FBSDKCodelessPathComponent *component = binding.path.lastObject;
|
||||
if ((component.section == -1 || component.section == indexPath.section)
|
||||
&& (component.row == -1 || component.row == indexPath.row)) {
|
||||
UICollectionViewCell *cell = [collectionView cellForItemAtIndexPath:indexPath];
|
||||
[binding trackEvent:cell];
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
[FBSDKSwizzler swizzleSelector:@selector(collectionView:didSelectItemAtIndexPath:)
|
||||
onClass:[delegate class]
|
||||
withBlock:block
|
||||
named:@"handle_collection_view"];
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
#pragma clang diagnostic pop
|
||||
- (void)updateBindings:(NSArray *)bindings {
|
||||
eventBindings = bindings;
|
||||
[reactBindings removeAllObjects];
|
||||
if (!isStarted) {
|
||||
[self start];
|
||||
}
|
||||
|
||||
fb_dispatch_on_main_thread(^{
|
||||
[self rematchBindings];
|
||||
});
|
||||
}
|
||||
|
||||
@end
|
||||
41
Example/Pods/FBSDKCoreKit/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/Codeless/FBSDKViewHierarchy.h
generated
Normal file
41
Example/Pods/FBSDKCoreKit/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/Codeless/FBSDKViewHierarchy.h
generated
Normal file
@ -0,0 +1,41 @@
|
||||
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
|
||||
//
|
||||
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
|
||||
// copy, modify, and distribute this software in source code or binary form for use
|
||||
// in connection with the web services and APIs provided by Facebook.
|
||||
//
|
||||
// As with any software that integrates with the Facebook platform, your use of
|
||||
// this software is subject to the Facebook Developer Principles and Policies
|
||||
// [http://developers.facebook.com/policy/]. This copyright 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 <Foundation/Foundation.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_SWIFT_NAME(ViewHierarchy)
|
||||
@interface FBSDKViewHierarchy : NSObject
|
||||
|
||||
+ (NSObject *)getParent:(NSObject *)obj;
|
||||
+ (NSArray<NSObject *> *)getChildren:(NSObject *)obj;
|
||||
+ (NSArray<NSObject *> *)getPath:(NSObject *)obj;
|
||||
+ (NSMutableDictionary<NSString *, id> *)getDetailAttributesOf:(NSObject *)obj;
|
||||
|
||||
+ (NSString *)getText:(NSObject *)obj;
|
||||
+ (NSString *)getHint:(NSObject *)obj;
|
||||
+ (NSIndexPath *)getIndexPath:(NSObject *)obj;
|
||||
+ (NSUInteger)getClassBitmask:(NSObject *)obj;
|
||||
+ (UITableView *)getParentTableView:(UIView *)cell;
|
||||
+ (UICollectionView *)getParentCollectionView:(UIView *)cell;
|
||||
+ (NSInteger)getTag:(NSObject *)obj;
|
||||
+ (NSNumber *)getViewReactTag:(UIView *)view;
|
||||
|
||||
+ (BOOL)isUserInputView:(NSObject *)obj;
|
||||
|
||||
@end
|
||||
640
Example/Pods/FBSDKCoreKit/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/Codeless/FBSDKViewHierarchy.m
generated
Normal file
640
Example/Pods/FBSDKCoreKit/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/Codeless/FBSDKViewHierarchy.m
generated
Normal file
@ -0,0 +1,640 @@
|
||||
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
|
||||
//
|
||||
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
|
||||
// copy, modify, and distribute this software in source code or binary form for use
|
||||
// in connection with the web services and APIs provided by Facebook.
|
||||
//
|
||||
// As with any software that integrates with the Facebook platform, your use of
|
||||
// this software is subject to the Facebook Developer Principles and Policies
|
||||
// [http://developers.facebook.com/policy/]. This copyright 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 "FBSDKViewHierarchy.h"
|
||||
|
||||
#import <objc/runtime.h>
|
||||
|
||||
#import <QuartzCore/QuartzCore.h>
|
||||
|
||||
#import <FBSDKCoreKit/FBSDKCoreKit+Internal.h>
|
||||
|
||||
#import "FBSDKCodelessMacros.h"
|
||||
#import "FBSDKCodelessPathComponent.h"
|
||||
|
||||
#define MAX_VIEW_HIERARCHY_LEVEL 35
|
||||
|
||||
typedef NS_ENUM(NSUInteger, FBCodelessClassBitmask) {
|
||||
/** Indicates that the class is subclass of UIControl */
|
||||
FBCodelessClassBitmaskUIControl = 1 << 3,
|
||||
/** Indicates that the class is subclass of UIControl */
|
||||
FBCodelessClassBitmaskUIButton = 1 << 4,
|
||||
/** Indicates that the class is ReactNative Button */
|
||||
FBCodelessClassBitmaskReactNativeButton = 1 << 6,
|
||||
/** Indicates that the class is UITableViewCell */
|
||||
FBCodelessClassBitmaskUITableViewCell = 1 << 7,
|
||||
/** Indicates that the class is UICollectionViewCell */
|
||||
FBCodelessClassBitmaskUICollectionViewCell = 1 << 8,
|
||||
/** Indicates that the class is UILabel */
|
||||
FBCodelessClassBitmaskLabel = 1 << 10,
|
||||
/** Indicates that the class is UITextView or UITextField*/
|
||||
FBCodelessClassBitmaskInput = 1 << 11,
|
||||
/** Indicates that the class is UIPicker*/
|
||||
FBCodelessClassBitmaskPicker = 1 << 12,
|
||||
/** Indicates that the class is UISwitch*/
|
||||
FBCodelessClassBitmaskSwitch = 1 << 13,
|
||||
/** Indicates that the class is UIViewController*/
|
||||
FBCodelessClassBitmaskUIViewController = 1 << 17,
|
||||
};
|
||||
|
||||
@implementation FBSDKViewHierarchy
|
||||
|
||||
+ (NSArray*)getChildren:(NSObject*)obj
|
||||
{
|
||||
if ([obj isKindOfClass:[UIControl class]]) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSMutableArray *children = [NSMutableArray array];
|
||||
|
||||
// children of window should be viewcontroller
|
||||
if ([obj isKindOfClass:[UIWindow class]]) {
|
||||
UIViewController *rootVC = ((UIWindow *)obj).rootViewController;
|
||||
NSArray<UIView *> *subviews = ((UIWindow *)obj).subviews;
|
||||
for (UIView *child in subviews) {
|
||||
if (child != rootVC.view) {
|
||||
UIViewController *vc = [FBSDKViewHierarchy getParentViewController:child];
|
||||
if (vc != nil && vc.view == child) {
|
||||
[children addObject:vc];
|
||||
} else {
|
||||
[children addObject:child];
|
||||
}
|
||||
} else {
|
||||
if (rootVC) {
|
||||
[children addObject:rootVC];
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if ([obj isKindOfClass:[UIView class]]) {
|
||||
NSArray<UIView *> *subviews = [((UIView *)obj).subviews copy];
|
||||
for (UIView *child in subviews) {
|
||||
UIViewController *vc = [FBSDKViewHierarchy getParentViewController:child];
|
||||
if (vc && vc.view == child) {
|
||||
[children addObject:vc];
|
||||
} else {
|
||||
[children addObject:child];
|
||||
}
|
||||
}
|
||||
} else if ([obj isKindOfClass:[UINavigationController class]]) {
|
||||
UIViewController *vc = ((UINavigationController*)obj).visibleViewController;
|
||||
UIViewController *tc = ((UINavigationController*)obj).topViewController;
|
||||
NSArray *nextChildren = [FBSDKViewHierarchy getChildren:((UIViewController*)obj).view];
|
||||
for (NSObject *child in nextChildren) {
|
||||
if (tc && [self isView:child superViewOfView:tc.view]) {
|
||||
[children addObject:tc];
|
||||
} else if (vc && [self isView:child superViewOfView:vc.view]) {
|
||||
[children addObject:vc];
|
||||
} else {
|
||||
if (child != vc.view && child != tc.view) {
|
||||
[children addObject:child];
|
||||
} else {
|
||||
if (vc && child == vc.view) {
|
||||
[children addObject:vc];
|
||||
} else if (tc && child == tc.view) {
|
||||
[children addObject:tc];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (vc && ![children containsObject:vc]) {
|
||||
[children addObject:vc];
|
||||
}
|
||||
} else if ([obj isKindOfClass:[UITabBarController class]]) {
|
||||
UIViewController *vc = ((UITabBarController *)obj).selectedViewController;
|
||||
NSArray *nextChildren = [FBSDKViewHierarchy getChildren:((UIViewController*)obj).view];
|
||||
for (NSObject *child in nextChildren) {
|
||||
if (vc && [self isView:child superViewOfView:vc.view]) {
|
||||
[children addObject:vc];
|
||||
} else {
|
||||
if (vc && child == vc.view) {
|
||||
[children addObject:vc];
|
||||
} else {
|
||||
[children addObject:child];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (vc && ![children containsObject:vc]) {
|
||||
[children addObject:vc];
|
||||
}
|
||||
} else if ([obj isKindOfClass:[UIViewController class]]) {
|
||||
UIViewController *vc = (UIViewController *)obj;
|
||||
if (vc.isViewLoaded) {
|
||||
NSArray *nextChildren = [FBSDKViewHierarchy getChildren:vc.view];
|
||||
if (nextChildren.count > 0) {
|
||||
[children addObjectsFromArray:nextChildren];
|
||||
}
|
||||
}
|
||||
for (NSObject *child in vc.childViewControllers) {
|
||||
[children addObject:child];
|
||||
}
|
||||
UIViewController *presentedVC = vc.presentedViewController;
|
||||
if (presentedVC) {
|
||||
[children addObject:presentedVC];
|
||||
}
|
||||
}
|
||||
return children;
|
||||
}
|
||||
|
||||
+ (NSObject *)getParent:(NSObject *)obj
|
||||
{
|
||||
if ([obj isKindOfClass:[UIView class]]) {
|
||||
UIView *superview = ((UIView *)obj).superview;
|
||||
UIViewController *superviewViewController = [FBSDKViewHierarchy
|
||||
getParentViewController:superview];
|
||||
if (superviewViewController && superviewViewController.view == superview) {
|
||||
return superviewViewController;
|
||||
}
|
||||
if (superview && superview != obj) {
|
||||
return superview;
|
||||
}
|
||||
}
|
||||
else if ([obj isKindOfClass:[UIViewController class]]) {
|
||||
UIViewController *vc = (UIViewController *)obj;
|
||||
UIViewController *parentVC = vc.parentViewController;
|
||||
UIViewController *presentingVC = vc.presentingViewController;
|
||||
UINavigationController *nav = vc.navigationController;
|
||||
UITabBarController *tab = vc.tabBarController;
|
||||
|
||||
if (nav) {
|
||||
return nav;
|
||||
}
|
||||
|
||||
if (tab) {
|
||||
return tab;
|
||||
}
|
||||
|
||||
if (parentVC) {
|
||||
return parentVC;
|
||||
}
|
||||
|
||||
if (presentingVC && presentingVC.presentedViewController == vc) {
|
||||
return presentingVC;
|
||||
}
|
||||
|
||||
// Return parent of view of UIViewController
|
||||
NSObject *viewParent = [FBSDKViewHierarchy getParent:vc.view];
|
||||
if (viewParent) {
|
||||
return viewParent;
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
+ (NSArray *)getPath:(NSObject *)obj
|
||||
{
|
||||
return [FBSDKViewHierarchy getPath:obj limit:MAX_VIEW_HIERARCHY_LEVEL];
|
||||
}
|
||||
|
||||
+ (NSArray *)getPath:(NSObject *)obj limit:(int)limit
|
||||
{
|
||||
if (!obj || limit <= 0) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSMutableArray *path;
|
||||
|
||||
NSObject *parent = [FBSDKViewHierarchy getParent:obj];
|
||||
if (parent) {
|
||||
NSArray *parentPath = [FBSDKViewHierarchy getPath:parent limit:limit - 1];
|
||||
path = [NSMutableArray arrayWithArray:parentPath];
|
||||
} else {
|
||||
path = [NSMutableArray array];
|
||||
}
|
||||
|
||||
NSDictionary *componentInfo = [FBSDKViewHierarchy getAttributesOf:obj parent:parent];
|
||||
|
||||
FBSDKCodelessPathComponent *pathComponent = [[FBSDKCodelessPathComponent alloc]
|
||||
initWithJSON:componentInfo];
|
||||
[path addObject:pathComponent];
|
||||
|
||||
return [NSArray arrayWithArray:path];
|
||||
}
|
||||
|
||||
+ (NSDictionary<NSString *, id> *)getAttributesOf:(NSObject *)obj parent:(NSObject *)parent
|
||||
{
|
||||
NSMutableDictionary *componentInfo = [NSMutableDictionary dictionary];
|
||||
componentInfo[CODELESS_MAPPING_CLASS_NAME_KEY] = NSStringFromClass([obj class]);
|
||||
|
||||
if (![FBSDKViewHierarchy isUserInputView:obj]) {
|
||||
NSString *text = [FBSDKViewHierarchy getText:obj];
|
||||
if (text) {
|
||||
componentInfo[CODELESS_MAPPING_TEXT_KEY] = text;
|
||||
}
|
||||
} else {
|
||||
componentInfo[CODELESS_MAPPING_TEXT_KEY] = @"";
|
||||
componentInfo[CODELESS_MAPPING_IS_USER_INPUT_KEY] = @YES;
|
||||
}
|
||||
|
||||
NSString *hint = [FBSDKViewHierarchy getHint:obj];
|
||||
if (hint) {
|
||||
componentInfo[CODELESS_MAPPING_HINT_KEY] = hint;
|
||||
}
|
||||
|
||||
NSIndexPath *indexPath = [FBSDKViewHierarchy getIndexPath:obj];
|
||||
if (indexPath) {
|
||||
componentInfo[CODELESS_MAPPING_SECTION_KEY] = @(indexPath.section);
|
||||
componentInfo[CODELESS_MAPPING_ROW_KEY] = @(indexPath.row);
|
||||
}
|
||||
|
||||
if (parent != nil) {
|
||||
NSArray *children = [FBSDKViewHierarchy getChildren:parent];
|
||||
NSUInteger index = [children indexOfObject:obj];
|
||||
if (index != NSNotFound) {
|
||||
componentInfo[CODELESS_MAPPING_INDEX_KEY] = @(index);
|
||||
}
|
||||
} else {
|
||||
componentInfo[CODELESS_MAPPING_INDEX_KEY] = @0;
|
||||
}
|
||||
|
||||
componentInfo[CODELESS_VIEW_TREE_TAG_KEY] = @([FBSDKViewHierarchy getTag:obj]);
|
||||
|
||||
return [componentInfo copy];
|
||||
}
|
||||
|
||||
+ (NSMutableDictionary<NSString *, id> *)getDetailAttributesOf:(NSObject *)obj
|
||||
{
|
||||
return [self getDetailAttributesOf:obj WithHash:YES];
|
||||
}
|
||||
|
||||
+ (NSMutableDictionary<NSString *, id> *)getDetailAttributesOf:(NSObject *)obj WithHash:(BOOL)hash
|
||||
{
|
||||
if (!obj) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSObject *parent = [FBSDKViewHierarchy getParent:obj];
|
||||
|
||||
NSDictionary *simpleAttributes = [FBSDKViewHierarchy getAttributesOf:obj parent:parent];
|
||||
|
||||
NSMutableDictionary *result = [NSMutableDictionary dictionaryWithDictionary:simpleAttributes];
|
||||
|
||||
NSString *className = NSStringFromClass([obj class]);
|
||||
result[CODELESS_VIEW_TREE_CLASS_NAME_KEY] = className;
|
||||
|
||||
NSUInteger classBitmask = [FBSDKViewHierarchy getClassBitmask:obj];
|
||||
result[CODELESS_VIEW_TREE_CLASS_TYPE_BIT_MASK_KEY] = [NSString stringWithFormat:@"%lu", (unsigned long)classBitmask];
|
||||
|
||||
if ([obj isKindOfClass:[UIControl class]]) {
|
||||
// Get actions of UIControl
|
||||
UIControl *control = (UIControl *)obj;
|
||||
NSMutableSet *actions = [NSMutableSet set];
|
||||
NSSet *targets = control.allTargets;
|
||||
for (NSObject *target in targets) {
|
||||
NSArray *ary = [control actionsForTarget:target forControlEvent:0];
|
||||
if (ary.count > 0) {
|
||||
[actions addObjectsFromArray:ary];
|
||||
}
|
||||
}
|
||||
if (targets.count > 0) {
|
||||
result[CODELESS_VIEW_TREE_ACTIONS_KEY] = actions.allObjects;
|
||||
}
|
||||
}
|
||||
|
||||
result[CODELESS_VIEW_TREE_DIMENSION_KEY] = [FBSDKViewHierarchy getDimensionOf:obj];
|
||||
|
||||
NSDictionary<NSString *, id> *textStyle = [FBSDKViewHierarchy getTextStyle:obj];
|
||||
if (textStyle) {
|
||||
result[CODELESS_VIEW_TREE_TEXT_STYLE_KEY] = textStyle;
|
||||
}
|
||||
|
||||
if (hash) {
|
||||
// hash text and hint
|
||||
result[CODELESS_VIEW_TREE_TEXT_KEY] = [FBSDKUtility SHA256Hash:result[CODELESS_VIEW_TREE_TEXT_KEY]];
|
||||
result[CODELESS_VIEW_TREE_HINT_KEY] = [FBSDKUtility SHA256Hash:result[CODELESS_VIEW_TREE_HINT_KEY]];
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
+ (NSIndexPath *)getIndexPath:(NSObject *)obj
|
||||
{
|
||||
NSIndexPath *indexPath = nil;
|
||||
|
||||
if ([obj isKindOfClass:[UITableViewCell class]]) {
|
||||
UITableView *tableView = [FBSDKViewHierarchy getParentTableView:(UIView *)obj];
|
||||
indexPath = [tableView indexPathForCell:(UITableViewCell *)obj];
|
||||
} else if ([obj isKindOfClass:[UICollectionViewCell class]]) {
|
||||
UICollectionView *collectionView = [FBSDKViewHierarchy getParentCollectionView:(UIView *)obj];
|
||||
indexPath = [collectionView indexPathForCell:(UICollectionViewCell *)obj];
|
||||
}
|
||||
|
||||
return indexPath;
|
||||
}
|
||||
|
||||
+ (NSString *)getText:(NSObject *)obj
|
||||
{
|
||||
NSString *text = nil;
|
||||
|
||||
if ([obj isKindOfClass:[UIButton class]]) {
|
||||
text = ((UIButton *)obj).currentTitle;
|
||||
} else if ([obj isKindOfClass:[UITextView class]] ||
|
||||
[obj isKindOfClass:[UITextField class]] ||
|
||||
[obj isKindOfClass:[UILabel class]]) {
|
||||
text = ((UILabel *)obj).text;
|
||||
} else if ([obj isKindOfClass:[UIPickerView class]]) {
|
||||
UIPickerView *picker = (UIPickerView *)obj;
|
||||
NSInteger sections = picker.numberOfComponents;
|
||||
NSMutableArray *titles = [NSMutableArray array];
|
||||
|
||||
for (NSInteger i = 0; i < sections; i++) {
|
||||
NSInteger row = [picker selectedRowInComponent:i];
|
||||
NSString *title;
|
||||
if ([picker.delegate
|
||||
respondsToSelector:@selector(pickerView:titleForRow:forComponent:)]) {
|
||||
title = [picker.delegate pickerView:picker titleForRow:row forComponent:i];
|
||||
} else if ([picker.delegate
|
||||
respondsToSelector:@selector(pickerView:attributedTitleForRow:forComponent:)]) {
|
||||
title = [picker.delegate
|
||||
pickerView:picker
|
||||
attributedTitleForRow:row forComponent:i].string;
|
||||
}
|
||||
[titles addObject:title ?: @""];
|
||||
}
|
||||
|
||||
if (titles.count > 0) {
|
||||
text = [FBSDKBasicUtility JSONStringForObject:titles
|
||||
error:NULL
|
||||
invalidObjectHandler:NULL];
|
||||
}
|
||||
} else if ([obj isKindOfClass:[UIDatePicker class]]) {
|
||||
UIDatePicker *picker = (UIDatePicker *)obj;
|
||||
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
|
||||
formatter.dateFormat = @"yyyy-MM-dd HH:mm:ssZ";
|
||||
text = [formatter stringFromDate:picker.date];
|
||||
} else if ([obj isKindOfClass:objc_lookUpClass("RCTTextView")]) {
|
||||
NSTextStorage *textStorage = [FBSDKAppEventsUtility getVariable:@"_textStorage"
|
||||
fromInstance:obj];
|
||||
if (textStorage) {
|
||||
text = textStorage.string;
|
||||
}
|
||||
} else if ([obj isKindOfClass:objc_lookUpClass("RCTBaseTextInputView")]) {
|
||||
NSAttributedString *attributedText = [FBSDKAppEventsUtility getVariable:@"attributedText"
|
||||
fromInstance:obj];
|
||||
text = attributedText.string;
|
||||
}
|
||||
|
||||
return text.length > 0 ? text : nil;
|
||||
}
|
||||
|
||||
+ (NSDictionary<NSString *, id> *)getTextStyle:(NSObject *)obj
|
||||
{
|
||||
UIFont *font = nil;
|
||||
if ([obj isKindOfClass:[UIButton class]]) {
|
||||
font = ((UIButton *)obj).titleLabel.font;
|
||||
} else if ([obj isKindOfClass:[UILabel class]]) {
|
||||
font = ((UILabel *)obj).font;
|
||||
} else if ([obj isKindOfClass:[UITextField class]]) {
|
||||
font = ((UITextField *)obj).font;
|
||||
} else if ([obj isKindOfClass:[UITextView class]]) {
|
||||
font = ((UITextView *)obj).font;
|
||||
}
|
||||
|
||||
if (font) {
|
||||
UIFontDescriptorSymbolicTraits traits = font.fontDescriptor.symbolicTraits;
|
||||
BOOL isBold = (traits & UIFontDescriptorTraitBold) != 0;
|
||||
BOOL isItalic = (traits & UIFontDescriptorTraitItalic) != 0;
|
||||
CGFloat fontSize = font.pointSize;
|
||||
|
||||
return @{
|
||||
CODELESS_VIEW_TREE_TEXT_IS_BOLD_KEY: @(isBold),
|
||||
CODELESS_VIEW_TREE_TEXT_IS_ITALIC_KEY: @(isItalic),
|
||||
CODELESS_VIEW_TREE_TEXT_SIZE_KEY: @(fontSize)
|
||||
};
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
+ (NSString *)getHint:(NSObject *)obj
|
||||
{
|
||||
NSString *hint = nil;
|
||||
|
||||
if ([obj isKindOfClass:[UITextField class]]) {
|
||||
hint = ((UITextField *)obj).placeholder;
|
||||
} else if ([obj isKindOfClass:[UINavigationController class]]) {
|
||||
UIViewController *top = ((UINavigationController *)obj).topViewController;
|
||||
if (top) {
|
||||
hint = NSStringFromClass([top class]);
|
||||
}
|
||||
}
|
||||
|
||||
return hint.length > 0 ? hint : nil;
|
||||
}
|
||||
|
||||
+ (NSUInteger)getClassBitmask:(NSObject *)obj
|
||||
{
|
||||
NSUInteger bitmask = 0;
|
||||
|
||||
if ([obj isKindOfClass:[UIView class]]) {
|
||||
if ([obj isKindOfClass:[UIControl class]]) {
|
||||
bitmask |= FBCodelessClassBitmaskUIControl;
|
||||
if ([obj isKindOfClass:[UIButton class]]) {
|
||||
bitmask |= FBCodelessClassBitmaskUIButton;
|
||||
} else if ([obj isKindOfClass:[UISwitch class]]) {
|
||||
bitmask |= FBCodelessClassBitmaskSwitch;
|
||||
}else if ([obj isKindOfClass:[UIDatePicker class]]) {
|
||||
bitmask |= FBCodelessClassBitmaskPicker;
|
||||
}
|
||||
} else if ([obj isKindOfClass:[UITableViewCell class]]) {
|
||||
bitmask |= FBCodelessClassBitmaskUITableViewCell;
|
||||
} else if ([obj isKindOfClass:[UICollectionViewCell class]]) {
|
||||
bitmask |= FBCodelessClassBitmaskUICollectionViewCell;
|
||||
} else if ([obj isKindOfClass:[UIPickerView class]]) {
|
||||
bitmask |= FBCodelessClassBitmaskPicker;
|
||||
} else if ([obj isKindOfClass:[UILabel class]]) {
|
||||
bitmask |= FBCodelessClassBitmaskLabel;
|
||||
}
|
||||
|
||||
if ([FBSDKViewHierarchy isRCTButton:((UIView *)obj)]) {
|
||||
bitmask |= FBCodelessClassBitmaskReactNativeButton;
|
||||
}
|
||||
|
||||
// Check selector of UITextInput protocol instead of checking conformsToProtocol
|
||||
if ([obj respondsToSelector:@selector(textInRange:)]) {
|
||||
bitmask |= FBCodelessClassBitmaskInput;
|
||||
}
|
||||
} else if ([obj isKindOfClass:[UIViewController class]]) {
|
||||
bitmask |= FBCodelessClassBitmaskUIViewController;
|
||||
}
|
||||
|
||||
return bitmask;
|
||||
}
|
||||
|
||||
+ (BOOL)isUserInputView:(NSObject *)obj
|
||||
{
|
||||
if (obj && [obj conformsToProtocol:@protocol(UITextInput)]) {
|
||||
id<UITextInput> input = (id<UITextInput>)obj;
|
||||
if ([input respondsToSelector:@selector(isSecureTextEntry)]
|
||||
&& input.secureTextEntry) {
|
||||
return YES;
|
||||
} else {
|
||||
if ([input respondsToSelector:@selector(keyboardType)]) {
|
||||
switch (input.keyboardType) {
|
||||
case UIKeyboardTypePhonePad:
|
||||
case UIKeyboardTypeEmailAddress:
|
||||
return YES;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NSString *text = [FBSDKViewHierarchy getText:obj];
|
||||
return text && [FBSDKAppEventsUtility isSensitiveUserData:text];
|
||||
}
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wundeclared-selector"
|
||||
+ (BOOL)isRCTButton:(UIView *)view
|
||||
{
|
||||
if (view == nil) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
Class classRCTView = objc_lookUpClass(ReactNativeClassRCTView);
|
||||
if (classRCTView && [view isKindOfClass:classRCTView] &&
|
||||
[view respondsToSelector:@selector(reactTagAtPoint:)] &&
|
||||
[view respondsToSelector:@selector(reactTag)] &&
|
||||
view.userInteractionEnabled) {
|
||||
// We check all its subviews locations and the view is clickable if there exists one that mathces reactTagAtPoint
|
||||
for (UIView *subview in view.subviews) {
|
||||
if (subview && ![subview isKindOfClass:classRCTView]) {
|
||||
NSNumber *reactTag = [view performSelector:@selector(reactTagAtPoint:)
|
||||
withObject:[NSValue valueWithCGPoint:subview.frame.origin]];
|
||||
NSNumber *subviewReactTag = [FBSDKViewHierarchy getViewReactTag:subview];
|
||||
if (reactTag != nil && subviewReactTag != nil && [reactTag isEqualToNumber:subviewReactTag]) {
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
+ (NSNumber *)getViewReactTag:(UIView *)view
|
||||
{
|
||||
if (view != nil && [view respondsToSelector:@selector(reactTag)]) {
|
||||
NSNumber *reactTag = [view performSelector:@selector(reactTag)];
|
||||
if (reactTag != nil && [reactTag isKindOfClass:[NSNumber class]]) {
|
||||
return reactTag;
|
||||
}
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
+ (BOOL)isView:(NSObject *)obj1 superViewOfView:(UIView *)obj2
|
||||
{
|
||||
if (![obj1 isKindOfClass:[UIView class]]
|
||||
|| ![obj2 isKindOfClass:[UIView class]]) {
|
||||
return NO;
|
||||
}
|
||||
UIView *view1 = (UIView *)obj1;
|
||||
UIView *view2 = (UIView *)obj2;
|
||||
UIView *superview = view2;
|
||||
while (superview) {
|
||||
superview = superview.superview;
|
||||
if (superview == view1) {
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
+ (UIViewController *)getParentViewController:(UIView *)view
|
||||
{
|
||||
UIResponder *parentResponder = view;
|
||||
|
||||
while (parentResponder) {
|
||||
parentResponder = parentResponder.nextResponder;
|
||||
if ([parentResponder isKindOfClass:[UIViewController class]]) {
|
||||
return (UIViewController *)parentResponder;
|
||||
}
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
+ (UITableView *)getParentTableView:(UIView *)cell
|
||||
{
|
||||
UIView *superview = cell.superview;
|
||||
while (superview) {
|
||||
if ([superview isKindOfClass:[UITableView class]]) {
|
||||
return (UITableView *)superview;
|
||||
}
|
||||
superview = superview.superview;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
+ (UICollectionView *)getParentCollectionView:(UIView *)cell
|
||||
{
|
||||
UIView *superview = cell.superview;
|
||||
while (superview) {
|
||||
if ([superview isKindOfClass:[UICollectionView class]]) {
|
||||
return (UICollectionView *)superview;
|
||||
}
|
||||
superview = superview.superview;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
+ (NSInteger)getTag:(NSObject *)obj
|
||||
{
|
||||
if ([obj isKindOfClass:[UIView class]]) {
|
||||
return ((UIView *)obj).tag;
|
||||
} else if ([obj isKindOfClass:[UIViewController class]]) {
|
||||
return ((UIViewController *)obj).view.tag;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
+ (NSDictionary<NSString *, NSNumber *> *)getDimensionOf:(NSObject *)obj
|
||||
{
|
||||
UIView *view = nil;
|
||||
|
||||
if ([obj isKindOfClass:[UIView class]]) {
|
||||
view = (UIView *)obj;
|
||||
} else if ([obj isKindOfClass:[UIViewController class]]) {
|
||||
view = ((UIViewController *)obj).view;
|
||||
}
|
||||
|
||||
CGRect frame = view.frame;
|
||||
CGPoint offset = CGPointZero;
|
||||
|
||||
if ([view isKindOfClass:[UIScrollView class]])
|
||||
offset = ((UIScrollView *)view).contentOffset;
|
||||
|
||||
return @{
|
||||
CODELESS_VIEW_TREE_TOP_KEY: @((int)frame.origin.y),
|
||||
CODELESS_VIEW_TREE_LEFT_KEY: @((int)frame.origin.x),
|
||||
CODELESS_VIEW_TREE_WIDTH_KEY: @((int)frame.size.width),
|
||||
CODELESS_VIEW_TREE_HEIGHT_KEY: @((int)frame.size.height),
|
||||
CODELESS_VIEW_TREE_OFFSET_X_KEY: @((int)offset.x),
|
||||
CODELESS_VIEW_TREE_OFFSET_Y_KEY: @((int)offset.y),
|
||||
CODELESS_VIEW_TREE_VISIBILITY_KEY: view.isHidden ? @4 : @0
|
||||
};
|
||||
}
|
||||
|
||||
@end
|
||||
237
Example/Pods/FBSDKCoreKit/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/FBSDKAppEvents+Internal.h
generated
Normal file
237
Example/Pods/FBSDKCoreKit/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/FBSDKAppEvents+Internal.h
generated
Normal file
@ -0,0 +1,237 @@
|
||||
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
|
||||
//
|
||||
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
|
||||
// copy, modify, and distribute this software in source code or binary form for use
|
||||
// in connection with the web services and APIs provided by Facebook.
|
||||
//
|
||||
// As with any software that integrates with the Facebook platform, your use of
|
||||
// this software is subject to the Facebook Developer Principles and Policies
|
||||
// [http://developers.facebook.com/policy/]. This copyright 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 <FBSDKCoreKit/FBSDKAppEvents.h>
|
||||
|
||||
#import "FBSDKAppEventsUtility.h"
|
||||
|
||||
@class FBSDKGraphRequest;
|
||||
|
||||
// Internally known event names
|
||||
|
||||
/** Use to log that the share dialog was launched */
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventNameShareSheetLaunch;
|
||||
|
||||
/** Use to log that the share dialog was dismissed */
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventNameShareSheetDismiss;
|
||||
|
||||
/** Use to log that the permissions UI was launched */
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventNamePermissionsUILaunch;
|
||||
|
||||
/** Use to log that the permissions UI was dismissed */
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventNamePermissionsUIDismiss;
|
||||
|
||||
/** Use to log that the login view was used */
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventNameLoginViewUsage;
|
||||
|
||||
/** Use to log that the share tray launched. */
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventNameShareTrayDidLaunch;
|
||||
|
||||
/** Use to log that the person selected a sharing target. */
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventNameShareTrayDidSelectActivity;
|
||||
|
||||
// Internally known event parameters
|
||||
|
||||
/** String parameter specifying the outcome of a dialog invocation */
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventParameterDialogOutcome;
|
||||
|
||||
/** Parameter key used to specify which application launches this application. */
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventParameterLaunchSource;
|
||||
|
||||
/** Use to log the result of a call to FBDialogs presentShareDialogWithParams: */
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventNameFBDialogsPresentShareDialog;
|
||||
|
||||
/** Use to log the result of a call to FBDialogs presentShareDialogWithOpenGraphActionParams: */
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventNameFBDialogsPresentShareDialogOG;
|
||||
|
||||
/** Use to log the result of a call to FBDialogs presentLikeDialogWithLikeParams: */
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventNameFBDialogsPresentLikeDialogOG;
|
||||
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventNameFBDialogsPresentShareDialogPhoto;
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventNameFBDialogsPresentMessageDialog;
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventNameFBDialogsPresentMessageDialogPhoto;
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventNameFBDialogsPresentMessageDialogOG;
|
||||
|
||||
/** Use to log the start of an auth request that cannot be fulfilled by the token cache */
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventNameFBSessionAuthStart;
|
||||
|
||||
/** Use to log the end of an auth request that was not fulfilled by the token cache */
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventNameFBSessionAuthEnd;
|
||||
|
||||
/** Use to log the start of a specific auth method as part of an auth request */
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventNameFBSessionAuthMethodStart;
|
||||
|
||||
/** Use to log the end of the last tried auth method as part of an auth request */
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventNameFBSessionAuthMethodEnd;
|
||||
|
||||
/** Use to log the timestamp for the transition to the Facebook native login dialog */
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventNameFBDialogsNativeLoginDialogStart;
|
||||
|
||||
/** Use to log the timestamp for the transition back to the app after the Facebook native login dialog */
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventNameFBDialogsNativeLoginDialogEnd;
|
||||
|
||||
/** Use to log the e2e timestamp metrics for web login */
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventNameFBDialogsWebLoginCompleted;
|
||||
|
||||
/** Use to log the result of the App Switch OS AlertView. Only available on OS >= iOS10 */
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventNameFBSessionFASLoginDialogResult;
|
||||
|
||||
/** Use to log the live streaming events from sdk */
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventNameFBSDKLiveStreamingStart;
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventNameFBSDKLiveStreamingStop;
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventNameFBSDKLiveStreamingPause;
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventNameFBSDKLiveStreamingResume;
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventNameFBSDKLiveStreamingError;
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventNameFBSDKLiveStreamingUpdateStatus;
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventNameFBSDKLiveStreamingVideoID;
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventNameFBSDKLiveStreamingMic;
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventNameFBSDKLiveStreamingCamera;
|
||||
|
||||
/** Use to log the results of a share dialog */
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventNameFBSDKEventShareDialogResult;
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventNameFBSDKEventMessengerShareDialogResult;
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventNameFBSDKEventAppInviteShareDialogResult;
|
||||
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventNameFBSDKEventShareDialogShow;
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventNameFBSDKEventMessengerShareDialogShow;
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventNameFBSDKEventAppInviteShareDialogShow;
|
||||
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventParameterDialogMode;
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventParameterDialogShareContentType;
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventParameterDialogShareContentUUID;
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventParameterDialogShareContentPageID;
|
||||
|
||||
/** Use to log parameters for share tray use */
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventParameterShareTrayActivityName;
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventParameterShareTrayResult;
|
||||
|
||||
/** Use to log parameters for live streaming*/
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventParameterLiveStreamingPrevStatus;
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventParameterLiveStreamingStatus;
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventParameterLiveStreamingError;
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventParameterLiveStreamingVideoID;
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventParameterLiveStreamingMicEnabled;
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventParameterLiveStreamingCameraEnabled;
|
||||
|
||||
// Internally known event parameter values
|
||||
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventsDialogOutcomeValue_Completed;
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventsDialogOutcomeValue_Cancelled;
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventsDialogOutcomeValue_Failed;
|
||||
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventsDialogShareContentTypeOpenGraph;
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventsDialogShareContentTypeStatus;
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventsDialogShareContentTypePhoto;
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventsDialogShareContentTypeVideo;
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventsDialogShareContentTypeCamera;
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventsDialogShareContentTypeMessengerGenericTemplate;
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventsDialogShareContentTypeMessengerMediaTemplate;
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventsDialogShareContentTypeMessengerOpenGraphMusicTemplate;
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventsDialogShareContentTypeUnknown;
|
||||
|
||||
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventsDialogShareModeAutomatic;
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventsDialogShareModeBrowser;
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventsDialogShareModeNative;
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventsDialogShareModeShareSheet;
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventsDialogShareModeWeb;
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventsDialogShareModeFeedBrowser;
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventsDialogShareModeFeedWeb;
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventsDialogShareModeUnknown;
|
||||
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventsNativeLoginDialogStartTime;
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventsNativeLoginDialogEndTime;
|
||||
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventsWebLoginE2E;
|
||||
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventNameFBSDKLikeButtonImpression;
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventNameFBSDKLoginButtonImpression;
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventNameFBSDKSendButtonImpression;
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventNameFBSDKShareButtonImpression;
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventNameFBSDKLiveStreamingButtonImpression;
|
||||
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventNameFBSDKSmartLoginService;
|
||||
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventNameFBSDKLikeButtonDidTap;
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventNameFBSDKLoginButtonDidTap;
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventNameFBSDKSendButtonDidTap;
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventNameFBSDKShareButtonDidTap;
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventNameFBSDKLiveStreamingButtonDidTap;
|
||||
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventNameFBSDKLikeControlDidDisable;
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventNameFBSDKLikeControlDidLike;
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventNameFBSDKLikeControlDidPresentDialog;
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventNameFBSDKLikeControlDidTap;
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventNameFBSDKLikeControlDidUnlike;
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventNameFBSDKLikeControlError;
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventNameFBSDKLikeControlImpression;
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventNameFBSDKLikeControlNetworkUnavailable;
|
||||
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventParameterDialogErrorMessage;
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventParameterLogTime;
|
||||
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventsWKWebViewMessagesHandlerKey;
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventsWKWebViewMessagesActionKey;
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventsWKWebViewMessagesEventKey;
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventsWKWebViewMessagesParamsKey;
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventsWKWebViewMessagesPixelTrackKey;
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventsWKWebViewMessagesPixelTrackCustomKey;
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventsWKWebViewMessagesPixelTrackSingleKey;
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventsWKWebViewMessagesPixelTrackSingleCustomKey;
|
||||
FOUNDATION_EXPORT NSString *const FBSDKAppEventsWKWebViewMessagesPixelIDKey;
|
||||
|
||||
@interface FBSDKAppEvents (Internal)
|
||||
|
||||
@property (class, nonatomic, strong, readonly) FBSDKAppEvents *singleton;
|
||||
|
||||
+ (void)logInternalEvent:(FBSDKAppEventName)eventName
|
||||
isImplicitlyLogged:(BOOL)isImplicitlyLogged;
|
||||
|
||||
+ (void)logInternalEvent:(FBSDKAppEventName)eventName
|
||||
valueToSum:(double)valueToSum
|
||||
isImplicitlyLogged:(BOOL)isImplicitlyLogged;
|
||||
|
||||
+ (void)logInternalEvent:(FBSDKAppEventName)eventName
|
||||
parameters:(NSDictionary *)parameters
|
||||
isImplicitlyLogged:(BOOL)isImplicitlyLogged;
|
||||
|
||||
+ (void)logInternalEvent:(FBSDKAppEventName)eventName
|
||||
parameters:(NSDictionary *)parameters
|
||||
isImplicitlyLogged:(BOOL)isImplicitlyLogged
|
||||
accessToken:(FBSDKAccessToken *)accessToken;
|
||||
|
||||
+ (void)logInternalEvent:(FBSDKAppEventName)eventName
|
||||
valueToSum:(double)valueToSum
|
||||
parameters:(NSDictionary *)parameters
|
||||
isImplicitlyLogged:(BOOL)isImplicitlyLogged;
|
||||
|
||||
+ (void)logInternalEvent:(NSString *)eventName
|
||||
valueToSum:(NSNumber *)valueToSum
|
||||
parameters:(NSDictionary *)parameters
|
||||
isImplicitlyLogged:(BOOL)isImplicitlyLogged
|
||||
accessToken:(FBSDKAccessToken *)accessToken;
|
||||
|
||||
+ (void)logImplicitEvent:(NSString *)eventName
|
||||
valueToSum:(NSNumber *)valueToSum
|
||||
parameters:(NSDictionary *)parameters
|
||||
accessToken:(FBSDKAccessToken *)accessToken;
|
||||
|
||||
- (void)flushForReason:(FBSDKAppEventsFlushReason)flushReason;
|
||||
- (void)registerNotifications;
|
||||
|
||||
@end
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user