├── checksum ├── BranchTestHost ├── Assets.xcassets │ ├── Contents.json │ └── AppIcon.appiconset │ │ └── Contents.json ├── ViewController.h ├── AppDelegate.h ├── BranchTestHost.entitlements ├── main.m ├── AppDelegate.m ├── ViewController.m └── Info.plist ├── Examples ├── TestBed-macOS │ ├── TestBed-macOS │ │ ├── Assets.xcassets │ │ │ ├── Contents.json │ │ │ ├── Branch.imageset │ │ │ │ ├── Branch-16.png │ │ │ │ ├── Branch-32.png │ │ │ │ ├── Branch-48.png │ │ │ │ └── Contents.json │ │ │ └── AppIcon.appiconset │ │ │ │ ├── AppIcon-128.png │ │ │ │ ├── AppIcon-16.png │ │ │ │ ├── AppIcon-256.png │ │ │ │ ├── AppIcon-32.png │ │ │ │ ├── AppIcon-512.png │ │ │ │ ├── AppIcon-128@2x.png │ │ │ │ ├── AppIcon-16@2x.png │ │ │ │ ├── AppIcon-256@2x.png │ │ │ │ ├── AppIcon-32@2x.png │ │ │ │ ├── AppIcon-512@2x.png │ │ │ │ └── Contents.json │ │ ├── main.m │ │ ├── APPAppDelegate.h │ │ ├── APPActionItemView.h │ │ ├── TestBed-macOS.entitlements │ │ ├── APPViewController.h │ │ ├── APPActionItemView.m │ │ ├── Info.plist │ │ └── APPV2EventSelectionWindow.xib │ ├── TestBed-macOSUITests │ │ ├── TestDeepLinking.zip │ │ ├── TestBed-macOS-UITest.entitlements │ │ ├── TestWebPage.html │ │ ├── TestDeferredLinking.html │ │ ├── TestNonBranchLink.html │ │ ├── TestRaceConditionWebPage.html │ │ ├── TestRedirectionWebPage.html │ │ ├── TestBedUIUtils.h │ │ ├── Info.plist │ │ ├── TestBedUITest.h │ │ ├── TestBedUIV2EventTest.m │ │ ├── TestBedUISetIdentityTest.m │ │ ├── TestBedUINonBranchLinkTest.m │ │ └── TestBedUIInstallOpenTest.m │ ├── TestBed-macOS.xcodeproj │ │ └── project.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ ├── README.md │ ├── TestBed-macOSTests │ │ ├── Info.plist │ │ └── TestBed-MacTests.m │ └── TestPlan.xctestplan └── TestDeepLinking │ ├── TestDeepLinking │ ├── Assets.xcassets │ │ ├── Contents.json │ │ ├── AccentColor.colorset │ │ │ └── Contents.json │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── main.m │ ├── TestDeepLinking.entitlements │ ├── AppDelegate.h │ ├── Info.plist │ └── AppDelegate.m │ ├── TestDeepLinking.xcodeproj │ └── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist │ ├── TestDeepLinkingTests │ ├── Info.plist │ └── TestDeepLinkingTests.m │ └── TestDeepLinkingUITests │ ├── Info.plist │ └── TestDeepLinkingUITests.m ├── README.md ├── Branch.xcodeproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── xcshareddata │ ├── IDETemplateMacros.plist │ └── xcschemes │ ├── Branch-macOS.xcscheme │ ├── Branch-framework.xcscheme │ └── BranchTestHost.xcscheme ├── Branch ├── Branch.m ├── BNCThreads.m ├── BranchHeader.m ├── Branch.modulemap ├── BNCUserAgentCollector.h ├── NSData+Branch.h ├── BNCQRCodeCache.h ├── BranchHeader.h ├── BranchDelegate.m ├── BranchMutableDictionary.h ├── BranchMainClass+Private.h ├── BNCEncoder.h ├── Branch.h ├── Info.plist ├── BNCNetworkInformation.h ├── NSString+Branch.h ├── BNCNetworkService.h ├── BNCLocalization.h ├── BranchLinkProperties.h ├── BNCPersistence.h ├── NSString+Branch.m ├── BNCURLBlackList.h ├── BNCQRCodeCache.m ├── BranchSession.h ├── BNCNetworkAPIService.h ├── BNCSettings.h ├── NSData+Branch.m ├── BranchError.h ├── BNCDevice.h ├── BNCThreads.h ├── BranchQRCode.h ├── BranchMutableDictionary.m ├── BranchLinkProperties.m ├── BranchSession.m ├── BNCKeyChain.h ├── BranchNetworkServiceProtocol.h ├── BNCApplication.h ├── BranchDelegate.h ├── BNCUserAgentCollector.m └── BranchError.m ├── SECURITY.md ├── Scripts ├── deploy-build-framework ├── deploy-checksum ├── deploy-git-tag ├── deploy-preflight ├── deploy-release └── whichapp ├── BranchTests ├── BranchTests-Bridging-Header.h ├── BranchCommerce.Test.m ├── Branch.Test.m ├── Info.plist ├── BNCTestCase.Test.m ├── BNCPersistence.Test.m ├── BNCNetworkInformation.Test.m ├── BNCTestCase.h ├── BNCTestNetworkService.Test.m ├── BranchUserTrackingDisabled.m ├── BNCTestNetworkService.h ├── NSData+Branch.Test.m ├── BranchMutableDictionary.Test.m ├── BNCDevice.Test.m ├── BranchLinkProperties.Test.m ├── BranchError.Test.m ├── BNCLocalization.Test.m ├── NSString+Branch.Test.m ├── BNCThreads.Test.m ├── BNCTestNetworkService.m ├── BNCApplication.Test.m ├── BNCKeyChain.Test.m └── BNCUserAgentCollectorTests.m ├── .github ├── ISSUE_TEMPLATE │ ├── feature-request.yml │ ├── config.yml │ └── bug-report.yml ├── workflows │ ├── stale.yml │ └── sync-readme-changelog.yml └── PULL_REQUEST_TEMPLATE.md ├── Package.swift ├── LICENSE ├── BranchMacOS.podspec ├── .gitignore └── CHANGELOG.md /checksum: -------------------------------------------------------------------------------- 1 | #checksum for Branch.zip on Github 2 | 7dabc397fcb4eab8dda7a858837a8832024170b0 Branch.zip 3 | -------------------------------------------------------------------------------- /BranchTestHost/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Examples/TestBed-macOS/TestBed-macOS/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Examples/TestDeepLinking/TestDeepLinking/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Branch SDK Documentation for Mac OS 2 | 3 | View [Branch's SDK documentation for Mac OS](https://help.branch.io/developers-hub/docs/mac-os-sdk-overview) 4 | -------------------------------------------------------------------------------- /Examples/TestBed-macOS/TestBed-macOSUITests/TestDeepLinking.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BranchMetrics/mac-branch-deep-linking/HEAD/Examples/TestBed-macOS/TestBed-macOSUITests/TestDeepLinking.zip -------------------------------------------------------------------------------- /Examples/TestBed-macOS/TestBed-macOS/Assets.xcassets/Branch.imageset/Branch-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BranchMetrics/mac-branch-deep-linking/HEAD/Examples/TestBed-macOS/TestBed-macOS/Assets.xcassets/Branch.imageset/Branch-16.png -------------------------------------------------------------------------------- /Examples/TestBed-macOS/TestBed-macOS/Assets.xcassets/Branch.imageset/Branch-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BranchMetrics/mac-branch-deep-linking/HEAD/Examples/TestBed-macOS/TestBed-macOS/Assets.xcassets/Branch.imageset/Branch-32.png -------------------------------------------------------------------------------- /Examples/TestBed-macOS/TestBed-macOS/Assets.xcassets/Branch.imageset/Branch-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BranchMetrics/mac-branch-deep-linking/HEAD/Examples/TestBed-macOS/TestBed-macOS/Assets.xcassets/Branch.imageset/Branch-48.png -------------------------------------------------------------------------------- /Branch.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Examples/TestBed-macOS/TestBed-macOS/Assets.xcassets/AppIcon.appiconset/AppIcon-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BranchMetrics/mac-branch-deep-linking/HEAD/Examples/TestBed-macOS/TestBed-macOS/Assets.xcassets/AppIcon.appiconset/AppIcon-128.png -------------------------------------------------------------------------------- /Examples/TestBed-macOS/TestBed-macOS/Assets.xcassets/AppIcon.appiconset/AppIcon-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BranchMetrics/mac-branch-deep-linking/HEAD/Examples/TestBed-macOS/TestBed-macOS/Assets.xcassets/AppIcon.appiconset/AppIcon-16.png -------------------------------------------------------------------------------- /Examples/TestBed-macOS/TestBed-macOS/Assets.xcassets/AppIcon.appiconset/AppIcon-256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BranchMetrics/mac-branch-deep-linking/HEAD/Examples/TestBed-macOS/TestBed-macOS/Assets.xcassets/AppIcon.appiconset/AppIcon-256.png -------------------------------------------------------------------------------- /Examples/TestBed-macOS/TestBed-macOS/Assets.xcassets/AppIcon.appiconset/AppIcon-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BranchMetrics/mac-branch-deep-linking/HEAD/Examples/TestBed-macOS/TestBed-macOS/Assets.xcassets/AppIcon.appiconset/AppIcon-32.png -------------------------------------------------------------------------------- /Examples/TestBed-macOS/TestBed-macOS/Assets.xcassets/AppIcon.appiconset/AppIcon-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BranchMetrics/mac-branch-deep-linking/HEAD/Examples/TestBed-macOS/TestBed-macOS/Assets.xcassets/AppIcon.appiconset/AppIcon-512.png -------------------------------------------------------------------------------- /Examples/TestBed-macOS/TestBed-macOS/Assets.xcassets/AppIcon.appiconset/AppIcon-128@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BranchMetrics/mac-branch-deep-linking/HEAD/Examples/TestBed-macOS/TestBed-macOS/Assets.xcassets/AppIcon.appiconset/AppIcon-128@2x.png -------------------------------------------------------------------------------- /Examples/TestBed-macOS/TestBed-macOS/Assets.xcassets/AppIcon.appiconset/AppIcon-16@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BranchMetrics/mac-branch-deep-linking/HEAD/Examples/TestBed-macOS/TestBed-macOS/Assets.xcassets/AppIcon.appiconset/AppIcon-16@2x.png -------------------------------------------------------------------------------- /Examples/TestBed-macOS/TestBed-macOS/Assets.xcassets/AppIcon.appiconset/AppIcon-256@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BranchMetrics/mac-branch-deep-linking/HEAD/Examples/TestBed-macOS/TestBed-macOS/Assets.xcassets/AppIcon.appiconset/AppIcon-256@2x.png -------------------------------------------------------------------------------- /Examples/TestBed-macOS/TestBed-macOS/Assets.xcassets/AppIcon.appiconset/AppIcon-32@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BranchMetrics/mac-branch-deep-linking/HEAD/Examples/TestBed-macOS/TestBed-macOS/Assets.xcassets/AppIcon.appiconset/AppIcon-32@2x.png -------------------------------------------------------------------------------- /Examples/TestBed-macOS/TestBed-macOS/Assets.xcassets/AppIcon.appiconset/AppIcon-512@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BranchMetrics/mac-branch-deep-linking/HEAD/Examples/TestBed-macOS/TestBed-macOS/Assets.xcassets/AppIcon.appiconset/AppIcon-512@2x.png -------------------------------------------------------------------------------- /Examples/TestBed-macOS/TestBed-macOS.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Examples/TestDeepLinking/TestDeepLinking.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Examples/TestDeepLinking/TestDeepLinking/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Branch/Branch.m: -------------------------------------------------------------------------------- 1 | /** 2 | @file Branch.h 3 | @package Branch 4 | @brief The Branch framework header. 5 | 6 | @author Edward Smith 7 | @date May 2018 8 | @copyright Copyright © 2018 Branch. All rights reserved. 9 | */ 10 | 11 | #import "Branch.h" 12 | -------------------------------------------------------------------------------- /Examples/TestBed-macOS/README.md: -------------------------------------------------------------------------------- 1 | # TestBed-Mac 2 | 3 | This is a quick example of using the Branch SDK in Mac apps. 4 | 5 | It's also is a testbed for the Branch Mac SDK unit and UI tests. 6 | 7 | ## Building & Running 8 | 9 | Open the TestBed-Mac.xcodeproj file in Xcode and select Project > Run from the menu. 10 | -------------------------------------------------------------------------------- /Branch.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Branch/BNCThreads.m: -------------------------------------------------------------------------------- 1 | /** 2 | @file BNCThreads.m 3 | @package Branch 4 | @brief Utilities for working with threads, queues, and blocks. 5 | 6 | @author Edward Smith 7 | @date May 2018 8 | @copyright Copyright © 2018 Branch. All rights reserved. 9 | */ 10 | 11 | #import "BNCThreads.h" 12 | -------------------------------------------------------------------------------- /Examples/TestBed-macOS/TestBed-macOS/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // TestBed-Mac 4 | // 5 | // Created by Edward Smith on 5/15/18. 6 | // Copyright © 2018 Edward Smith. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | int main(int argc, const char * argv[]) { 12 | return NSApplicationMain(argc, argv); 13 | } 14 | -------------------------------------------------------------------------------- /Examples/TestBed-macOS/TestBed-macOSUITests/TestBed-macOS-UITest.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Branch/BranchHeader.m: -------------------------------------------------------------------------------- 1 | /** 2 | @file BranchHeader.h 3 | @package Branch 4 | @brief Imports the system header dependencies for the Branch SDK. 5 | 6 | @author Edward Smith 7 | @date May 2018 8 | @copyright Copyright © 2018 Branch. All rights reserved. 9 | */ 10 | 11 | #import "BranchHeader.h" 12 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Reporting a Vulnerability 4 | If you discover a potential security issue in this project we ask that you notify Branch Security directly via email to security@branch.io 5 | Please do not open GitHub issues or pull requests - this makes the problem immediately visible to everyone, including malicious actors. 6 | -------------------------------------------------------------------------------- /Examples/TestBed-macOS/TestBed-macOS.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Examples/TestDeepLinking/TestDeepLinking.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Scripts/deploy-build-framework: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euo pipefail 3 | 4 | # deploy-build-framework 5 | # 6 | # Edward Smith, August 2017 7 | 8 | scriptname=$(basename "${BASH_SOURCE[0]}") 9 | scriptpath="${BASH_SOURCE[0]}" 10 | scriptpath=$(cd "$(dirname "${scriptpath}")" && pwd) 11 | cd ${scriptpath}/../ 12 | 13 | # Build 14 | xcodebuild -scheme 'Branch-framework' 15 | -------------------------------------------------------------------------------- /Examples/TestBed-macOS/TestBed-macOS/APPAppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // APPAppDelegate.h 3 | // TestBed-Mac 4 | // 5 | // Created by Edward Smith on 5/15/18. 6 | // Copyright © 2018 Edward Smith. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface APPAppDelegate : NSObject 12 | - (void) processLogMessage:(NSString*)message; 13 | @end 14 | -------------------------------------------------------------------------------- /Examples/TestDeepLinking/TestDeepLinking/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // TestDeepLinking 4 | // 5 | // Created by Nidhi on 2/3/21. 6 | // 7 | 8 | #import 9 | 10 | int main(int argc, const char * argv[]) { 11 | @autoreleasepool { 12 | // Setup code that might create autoreleased objects goes here. 13 | } 14 | return NSApplicationMain(argc, argv); 15 | } 16 | -------------------------------------------------------------------------------- /Branch/Branch.modulemap: -------------------------------------------------------------------------------- 1 | /** 2 | @file Branch.modulemap 3 | @package Branch 4 | @brief The Branch SDK module map file. 5 | 6 | @author Edward Smith 7 | @date May 2018 8 | @copyright Copyright © 2018 Branch. All rights reserved. 9 | */ 10 | 11 | framework module Branch { 12 | umbrella header "Branch.h" 13 | export * 14 | module * { export * } 15 | } 16 | -------------------------------------------------------------------------------- /Examples/TestBed-macOS/TestBed-macOS/APPActionItemView.h: -------------------------------------------------------------------------------- 1 | // 2 | // APPActionItemView.h 3 | // TestBed-Mac 4 | // 5 | // Created by Edward on 5/30/18. 6 | // Copyright © 2018 Branch. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface APPActionItemView : NSCollectionViewItem 13 | @property (weak) IBOutlet NSTextField *detailTextField; 14 | @end 15 | -------------------------------------------------------------------------------- /BranchTests/BranchTests-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | /** 2 | @file BranchTests-Bridging-Header.h 3 | @package BranchTests 4 | @brief The bridging header for the BranchTests target. 5 | 6 | @author Edward Smith 7 | @date June 10, 2018 8 | @copyright Copyright © 2018 Branch. All rights reserved. 9 | */ 10 | 11 | #import "Branch.h" 12 | #import "BNCTestCase.h" 13 | #import "BNCThreads.h" 14 | -------------------------------------------------------------------------------- /BranchTestHost/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | /** 3 | @file ViewController.h 4 | @package BranchTestHost 5 | @brief < A brief description of the file function. > 6 | 7 | @author Ernest Cho 8 | @date 2020 9 | @copyright Copyright © 2020 Branch. All rights reserved. 10 | */ 11 | 12 | #import 13 | 14 | @interface ViewController : NSViewController 15 | 16 | 17 | @end 18 | 19 | -------------------------------------------------------------------------------- /BranchTestHost/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | /** 3 | @file AppDelegate.h 4 | @package BranchTestHost 5 | @brief < A brief description of the file function. > 6 | 7 | @author Ernest Cho 8 | @date 2020 9 | @copyright Copyright © 2020 Branch. All rights reserved. 10 | */ 11 | 12 | #import 13 | 14 | @interface AppDelegate : NSObject 15 | 16 | 17 | @end 18 | 19 | -------------------------------------------------------------------------------- /Examples/TestDeepLinking/TestDeepLinking/TestDeepLinking.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.developer.associated-domains 6 | 7 | wfnz6.app.link 8 | wfnz6-alternate.app.link 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /BranchTestHost/BranchTestHost.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.files.user-selected.read-only 8 | 9 | com.apple.security.network.client 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /Examples/TestBed-macOS/TestBed-macOSUITests/TestWebPage.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Branch SDK : macOS 7 | 8 | 9 | 10 |

Branch SDK for macOS

11 | Open in the app! 12 | 13 | 14 | -------------------------------------------------------------------------------- /Examples/TestBed-macOS/TestBed-macOSUITests/TestDeferredLinking.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Branch SDK : macOS 7 | 8 | 9 | 10 |

Branch SDK for macOS

11 | Open in the app! 12 | 13 | 14 | -------------------------------------------------------------------------------- /Examples/TestBed-macOS/TestBed-macOSUITests/TestNonBranchLink.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Branch SDK : macOS 7 | 8 | 9 | 10 |

Branch SDK for macOS - Test Non-Branch link

11 | Open in the app! 12 | 13 | 14 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: 🚀 Feature Request 3 | description: Suggest an idea for this project 4 | title: "(short issue description)" 5 | labels: [feature-request, needs-triage] 6 | assignees: [] 7 | body: 8 | - type: textarea 9 | id: description 10 | attributes: 11 | label: Describe the feature 12 | description: A clear and concise description of the feature you are proposing. 13 | validations: 14 | required: true 15 | -------------------------------------------------------------------------------- /Examples/TestBed-macOS/TestBed-macOSUITests/TestRaceConditionWebPage.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Branch SDK : macOS 7 | 8 | 9 | 10 |

Branch SDK for macOS

11 | Open in the app ( Select cancel to open in Web SDK)! 12 | 13 | 14 | -------------------------------------------------------------------------------- /Examples/TestBed-macOS/TestBed-macOSUITests/TestRedirectionWebPage.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Branch SDK : macOS 7 | 8 | 9 | 10 |

Branch SDK for macOS

11 | Open in the app! ($afterclick_desktop_url param added.) 12 | 13 | 14 | -------------------------------------------------------------------------------- /Examples/TestDeepLinking/TestDeepLinking/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // TestDeepLinking 4 | // 5 | // Created by Nidhi on 2/3/21. 6 | // 7 | 8 | #import 9 | 10 | @interface AppDelegate : NSObject 11 | 12 | @property (weak) IBOutlet NSTextField *notification; 13 | @property (weak) IBOutlet NSScrollView *logs; 14 | @property (unsafe_unretained) IBOutlet NSTextView *sessionData; 15 | 16 | - (void) processLogMessage:(NSString*)message; 17 | 18 | @end 19 | 20 | -------------------------------------------------------------------------------- /BranchTestHost/main.m: -------------------------------------------------------------------------------- 1 | // 2 | /** 3 | @file main.m 4 | @package BranchTestHost 5 | @brief < A brief description of the file function. > 6 | 7 | @author Ernest Cho 8 | @date 2020 9 | @copyright Copyright © 2020 Branch. All rights reserved. 10 | */ 11 | 12 | #import 13 | 14 | int main(int argc, const char * argv[]) { 15 | @autoreleasepool { 16 | // Setup code that might create autoreleased objects goes here. 17 | } 18 | return NSApplicationMain(argc, argv); 19 | } 20 | -------------------------------------------------------------------------------- /Examples/TestBed-macOS/TestBed-macOS/Assets.xcassets/Branch.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Branch-16.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "Branch-32.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "Branch-48.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | --- 2 | blank_issues_enabled: false 3 | contact_links: 4 | - name: "📕 Documentation Issue" 5 | url: https://help.branch.io/developers-hub/docs/mac-os-sdk-overview 6 | about: Report an issue in the Branch macOS SDK Reference documentation by clicking "Suggest edits" button on the documentation page. 7 | - name: "Branch Support" 8 | url: https://help.branch.io/using-branch/page/submit-a-ticket 9 | about: If you are having general trouble with Branch macOS SDK integration, please submit a ticket to Branch Support. 10 | -------------------------------------------------------------------------------- /Branch/BNCUserAgentCollector.h: -------------------------------------------------------------------------------- 1 | // 2 | // BNCUserAgentCollector.h 3 | // Branch 4 | // 5 | // Created by Ernest Cho on 8/29/19. 6 | // Copyright © 2019 Branch, Inc. All rights reserved. 7 | // 8 | 9 | @import Foundation; 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface BNCUserAgentCollector : NSObject 14 | 15 | + (BNCUserAgentCollector *)instance; 16 | 17 | @property (nonatomic, copy, readwrite) NSString *userAgent; 18 | 19 | - (void)loadUserAgentWithCompletion:(void (^)(NSString * _Nullable userAgent))completion; 20 | 21 | @end 22 | 23 | NS_ASSUME_NONNULL_END 24 | -------------------------------------------------------------------------------- /Branch.xcodeproj/xcshareddata/IDETemplateMacros.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | FILEHEADER 6 | 7 | /** 8 | @file ___FILENAME___ 9 | @package ___PACKAGENAME___ 10 | @brief < A brief description of the file function. > 11 | 12 | @author ___FULLUSERNAME___ 13 | @date ___YEAR___ 14 | @copyright ___COPYRIGHT___ 15 | */ 16 | 17 | 18 | -------------------------------------------------------------------------------- /Branch/NSData+Branch.h: -------------------------------------------------------------------------------- 1 | /** 2 | @file NSData+Branch.h 3 | @package Branch 4 | @brief NSData additions. 5 | 6 | @author Edward Smith 7 | @date June 2018 8 | @copyright Copyright © 2018 Branch. All rights reserved. 9 | */ 10 | 11 | #import 12 | 13 | NS_ASSUME_NONNULL_BEGIN 14 | 15 | @interface NSData (Branch) 16 | + (NSData*) bnc_dataWithHexString:(NSString*)string; 17 | @end 18 | 19 | FOUNDATION_EXPORT void BNCForceNSDataCategoryToLoad(void) 20 | __attribute__((constructor)); 21 | 22 | NS_ASSUME_NONNULL_END 23 | -------------------------------------------------------------------------------- /Branch/BNCQRCodeCache.h: -------------------------------------------------------------------------------- 1 | // 2 | // BNCQRCodeCache.h 3 | // BranchMacOS 4 | // 5 | // Created by Nipun Singh on 5/24/22. 6 | // Copyright © 2022 Branch, Inc. All rights reserved. 7 | // 8 | 9 | #import "BranchHeader.h" 10 | 11 | #ifndef BNCQRCodeCache_h 12 | #define BNCQRCodeCache_h 13 | 14 | @interface BNCQRCodeCache : NSObject 15 | 16 | + (BNCQRCodeCache *) sharedInstance; 17 | - (void)addQRCodeToCache:(NSData *)qrCodeData withParams:(NSMutableDictionary *)parameters; 18 | - (NSData *)checkQRCodeCache:(NSMutableDictionary *)parameters; 19 | 20 | @end 21 | 22 | #endif /* BNCQRCodeCache_h */ 23 | -------------------------------------------------------------------------------- /Branch/BranchHeader.h: -------------------------------------------------------------------------------- 1 | /** 2 | @file BranchHeader.h 3 | @package Branch 4 | @brief Imports the system header dependencies for the Branch SDK. 5 | 6 | @author Edward Smith 7 | @date May 2018 8 | @copyright Copyright © 2018 Branch. All rights reserved. 9 | */ 10 | 11 | #ifndef BranchHeader_h 12 | #define BranchHeader_h 13 | 14 | #if __has_feature(modules) 15 | @import Foundation; 16 | #else 17 | #import 18 | #endif 19 | 20 | #if __has_feature(modules) 21 | @import AppKit; 22 | #else 23 | #import 24 | #endif 25 | 26 | #endif // BranchHeader_h 27 | -------------------------------------------------------------------------------- /BranchTests/BranchCommerce.Test.m: -------------------------------------------------------------------------------- 1 | /** 2 | @file BranchCommerce.Test.m 3 | @package BranchTests 4 | @brief BranchCommerce Tests 5 | 6 | @author Edward Smith 7 | @date May 2018 8 | @copyright Copyright © 2018 Branch. All rights reserved. 9 | */ 10 | 11 | #import "BNCTestCase.h" 12 | #import "BranchCommerce.h" 13 | 14 | @interface BranchCommerceTest : BNCTestCase 15 | @end 16 | 17 | @implementation BranchCommerceTest 18 | 19 | - (void)testBranchCommerce { 20 | XCTAssertTrue(BNCProductCategoryAllCategories().count == 21); 21 | XCTAssertTrue(BNCCurrencyAllCurrencies().count == 178); 22 | } 23 | 24 | @end 25 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.3 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | import PackageDescription 5 | 6 | let package = Package( 7 | name: "Branch", 8 | platforms: [ 9 | .macOS(.v10_14) 10 | ], 11 | products: [ 12 | .library( 13 | name: "Branch", 14 | targets: ["Branch"]), 15 | ], 16 | targets: [ 17 | .target( 18 | name: "Branch", 19 | path: "Branch", 20 | publicHeadersPath: ".", 21 | cSettings: [ 22 | .headerSearchPath("../"), 23 | ] 24 | ), 25 | ] 26 | ) 27 | 28 | -------------------------------------------------------------------------------- /BranchTestHost/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | /** 3 | @file AppDelegate.m 4 | @package BranchTestHost 5 | @brief < A brief description of the file function. > 6 | 7 | @author Ernest Cho 8 | @date 2020 9 | @copyright Copyright © 2020 Branch. All rights reserved. 10 | */ 11 | 12 | #import "AppDelegate.h" 13 | 14 | @interface AppDelegate () 15 | 16 | @end 17 | 18 | @implementation AppDelegate 19 | 20 | - (void)applicationDidFinishLaunching:(NSNotification *)aNotification { 21 | // Insert code here to initialize your application 22 | } 23 | 24 | 25 | - (void)applicationWillTerminate:(NSNotification *)aNotification { 26 | // Insert code here to tear down your application 27 | } 28 | 29 | 30 | @end 31 | -------------------------------------------------------------------------------- /Examples/TestBed-macOS/TestBed-macOS/TestBed-macOS.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.developer.associated-domains 6 | 7 | applinks:testbed-mac.app.link 8 | 9 | com.apple.security.app-sandbox 10 | 11 | com.apple.security.files.user-selected.read-only 12 | 13 | com.apple.security.network.client 14 | 15 | keychain-access-groups 16 | 17 | $(AppIdentifierPrefix)io.branch.sdk.TestBed-Mac 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /BranchTestHost/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | /** 3 | @file ViewController.m 4 | @package BranchTestHost 5 | @brief < A brief description of the file function. > 6 | 7 | @author Ernest Cho 8 | @date 2020 9 | @copyright Copyright © 2020 Branch. All rights reserved. 10 | */ 11 | 12 | #import "ViewController.h" 13 | 14 | @implementation ViewController 15 | 16 | // Comment for test commit 17 | - (void)viewDidLoad { 18 | [super viewDidLoad]; 19 | 20 | // Do any additional setup after loading the view. 21 | } 22 | 23 | 24 | - (void)setRepresentedObject:(id)representedObject { 25 | [super setRepresentedObject:representedObject]; 26 | 27 | // Update the view, if already loaded. 28 | } 29 | 30 | 31 | @end 32 | -------------------------------------------------------------------------------- /Examples/TestBed-macOS/TestBed-macOSUITests/TestBedUIUtils.h: -------------------------------------------------------------------------------- 1 | // 2 | // TestBedUIUtils.h 3 | // TestBed-macOSUITests 4 | // 5 | // Created by Nidhi on 11/3/20. 6 | // Copyright © 2020 Branch. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | #define TESTBED_CLICK_LINK "https://testbed-mac.app.link/ODYeswaVWM" 14 | #define TESTBED_CLICK_LINK_WITH_REDIRECTION "https://testbed-mac.app.link/y94TsHlrAdb" 15 | #define TESTBED_CLICK_LINK_RACE_CONDN "https://testbed-mac.app.link/KnH4lWDO6bb" 16 | 17 | @interface TestBedUIUtils : NSObject 18 | 19 | + (NSDictionary *) dictionaryFromString:(NSString *)APIDataString; 20 | + (void) deleteSettingsFiles; 21 | 22 | @end 23 | 24 | NS_ASSUME_NONNULL_END 25 | -------------------------------------------------------------------------------- /Scripts/deploy-checksum: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euo pipefail 3 | 4 | # deploy-checksum - The zip & checksum the framework. 5 | # 6 | # Edward Smith, December 2016 7 | 8 | scriptname=$(basename "${BASH_SOURCE[0]}") 9 | scriptpath="${BASH_SOURCE[0]}" 10 | scriptpath=$(cd "$(dirname "${scriptpath}")" && pwd) 11 | cd ${scriptpath}/../build 12 | 13 | # Zip the SDK files 14 | echo "Zipping frameworks" 15 | zip -rqy Branch.zip Branch.framework/ 16 | 17 | # Checksum the zip files 18 | echo "Creating checksums" 19 | checksum_file=checksum 20 | 21 | echo '#checksum for Branch.zip on Github' > "$checksum_file" 22 | shasum Branch.zip >> $checksum_file 23 | 24 | # remove source frameworks 25 | mv Branch.zip .. 26 | mv checksum .. 27 | echo "Cleaning up" 28 | rm -rf Branch.framework 29 | 30 | -------------------------------------------------------------------------------- /BranchTests/Branch.Test.m: -------------------------------------------------------------------------------- 1 | /** 2 | @file Branch.Test.m 3 | @package BranchTests 4 | @brief Branch frame work tests. 5 | 6 | @author Edward Smith 7 | @date May 2018 8 | @copyright Copyright © 2018 Branch. All rights reserved. 9 | */ 10 | 11 | #import "BNCTestCase.h" 12 | #import "Branch.h" 13 | 14 | @interface BranchTest : BNCTestCase 15 | @end 16 | 17 | @implementation BranchTest 18 | 19 | - (void)testVersion { 20 | double vn = BranchVersionNumber; 21 | const unsigned char *vs = BranchVersionString; 22 | XCTAssertTrue(vn > 0 && vs); 23 | NSString*testString = [[NSString alloc] initWithUTF8String:(const char*)vs]; 24 | XCTAssertTrue([testString hasPrefix:@"@(#)PROGRAM:Branch PROJECT:Branch-"]); 25 | } 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /Branch/BranchDelegate.m: -------------------------------------------------------------------------------- 1 | /** 2 | @file BranchDelegate.m 3 | @package Branch 4 | @brief Branch delegate protocol and notifications. 5 | 6 | @author Edward Smith 7 | @date June 30, 2017 8 | @copyright Copyright © 2017 Branch. All rights reserved. 9 | */ 10 | 11 | #import "BranchDelegate.h" 12 | 13 | NSString* const BranchWillStartSessionNotification = @"BranchWillStartSessionNotification"; 14 | NSString* const BranchDidStartSessionNotification = @"BranchDidStartSessionNotification"; 15 | NSString* const BranchDidOpenURLWithSessionNotification = @"BranchDidOpenURLWithSessionNotification"; 16 | 17 | NSString* const BranchErrorKey = @"BranchErrorKey"; 18 | NSString* const BranchURLKey = @"BranchURLKey"; 19 | NSString* const BranchSessionKey = @"BranchSessionKey"; 20 | -------------------------------------------------------------------------------- /Examples/TestBed-macOS/TestBed-macOSTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /Examples/TestBed-macOS/TestBed-macOSUITests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /Branch/BranchMutableDictionary.h: -------------------------------------------------------------------------------- 1 | /** 2 | @file BranchMutableDictionary.h 3 | @package Branch 4 | @brief A thread-safe mutable dictionary. 5 | 6 | @author Edward Smith 7 | @date July 2018 8 | @copyright Copyright © 2018 Branch. All rights reserved. 9 | */ 10 | 11 | #import 12 | 13 | 14 | #ifndef BranchMutableDictionary_h 15 | #define BranchMutableDictionary_h 16 | 17 | /** 18 | This is a thread-safe version of an NSMutableDictionary. 19 | */ 20 | @interface BranchMutableDictionary : NSMutableDictionary 21 | - (instancetype) init NS_DESIGNATED_INITIALIZER; 22 | - (instancetype) initWithCapacity:(NSUInteger)numItems NS_DESIGNATED_INITIALIZER; 23 | - (instancetype) initWithCoder:(NSCoder *)aDecoder NS_DESIGNATED_INITIALIZER; 24 | @end 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /Examples/TestDeepLinking/TestDeepLinkingTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /Examples/TestDeepLinking/TestDeepLinkingUITests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /BranchTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | AppIdentifierPrefix 22 | $(AppIdentifierPrefix) 23 | 24 | 25 | -------------------------------------------------------------------------------- /Branch/BranchMainClass+Private.h: -------------------------------------------------------------------------------- 1 | /** 2 | @file BranchMainClass+Private.h 3 | @package Branch 4 | @brief Private definitions for the Branch class. 5 | 6 | @author Edward Smith 7 | @date June 2018 8 | @copyright Copyright © 2018 Branch. All rights reserved. 9 | */ 10 | 11 | #import "BranchMainClass.h" 12 | @class BNCNetworkAPIService, BNCSettings; 13 | 14 | NS_ASSUME_NONNULL_BEGIN 15 | 16 | @interface Branch (Private) 17 | @property (atomic, strong, readonly) BNCNetworkAPIService*_Nullable networkAPIService; 18 | @property (atomic, strong, readonly) BranchConfiguration*_Nullable configuration; 19 | - (void) clearAllSettings; 20 | - (void) startNewSession; 21 | - (void) endSession; 22 | @end 23 | 24 | @interface BranchConfiguration (Private) 25 | @property (atomic, strong, readonly) BNCSettings* settings; 26 | @end 27 | 28 | NS_ASSUME_NONNULL_END 29 | -------------------------------------------------------------------------------- /Branch/BNCEncoder.h: -------------------------------------------------------------------------------- 1 | /** 2 | @file BNCEncoder.h 3 | @package Branch 4 | @brief A light weight, general purpose object encoder. 5 | 6 | @author Edward Smith 7 | @date June 2018 8 | @copyright Copyright © 2018 Branch. All rights reserved. 9 | */ 10 | 11 | #import 12 | 13 | NS_ASSUME_NONNULL_BEGIN 14 | 15 | @interface BNCEncoder : NSObject 16 | 17 | + (NSError*_Nullable) decodeInstance:(id)instance 18 | withCoder:(NSCoder*)coder 19 | ignoring:(NSArray*_Nullable)ignoreIvars; 20 | 21 | + (NSError*_Nullable) encodeInstance:(id)instance 22 | withCoder:(NSCoder*)coder 23 | ignoring:(NSArray*_Nullable)ignoreIvars; 24 | 25 | + (NSError*) copyInstance:(id)toInstance 26 | fromInstance:(id)fromInstance 27 | ignoring:(NSArray*_Nullable)ignoreIvarsArray; 28 | 29 | @end 30 | 31 | NS_ASSUME_NONNULL_END 32 | -------------------------------------------------------------------------------- /Scripts/deploy-git-tag: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euo pipefail 3 | 4 | # deploy-git-tag - Commit and push the current git changes. Create a version tag and push to master. 5 | # 6 | # Edward Smith, January 2017 7 | 8 | scriptfile="$( cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 9 | scriptfile="${scriptfile}"/$(basename "$0") 10 | cd $(dirname "$scriptfile")/.. 11 | 12 | git_branch=$(git symbolic-ref --short HEAD) 13 | version=$(./scripts/version) 14 | 15 | echo ">>> Merging and pushing '$git_branch' to master..." 1>&2 16 | 17 | # Tag and merge the release to master: 18 | git add --all 19 | git commit -m "Release ${version}." 20 | git push 21 | git checkout master 22 | git pull 23 | git merge -m "Merge ${git_branch}." origin "${git_branch}" 24 | (git commit || true) 25 | git tag "${version}" 26 | git push 27 | git push --tags origin master 28 | 29 | # Update staging from master: 30 | git checkout staging 31 | git pull 32 | git pull origin master 33 | git push 34 | -------------------------------------------------------------------------------- /Branch/Branch.h: -------------------------------------------------------------------------------- 1 | /** 2 | @file Branch.h 3 | @package Branch 4 | @brief The Branch framework header. 5 | 6 | @author Edward Smith 7 | @date May 2018 8 | @copyright Copyright © 2018 Branch. All rights reserved. 9 | */ 10 | 11 | #import "BranchHeader.h" 12 | 13 | /// The Branch SDK version number as a double. 14 | FOUNDATION_EXPORT double BranchVersionNumber; 15 | 16 | /// The Branch SDK version framework string. 17 | FOUNDATION_EXPORT const unsigned char BranchVersionString[]; 18 | 19 | #import "BranchCommerce.h" 20 | #import "BranchDelegate.h" 21 | #import "BranchError.h" 22 | #import "BranchEvent.h" 23 | #import "BranchLinkProperties.h" 24 | #import "BranchNetworkServiceProtocol.h" 25 | #import "BranchMainClass.h" 26 | #import "BranchMutableDictionary.h" 27 | #import "BranchSession.h" 28 | #import "BranchUniversalObject.h" 29 | #import "BranchQRCode.h" 30 | 31 | // Exposed private headers: 32 | #import "BNCLog.h" 33 | -------------------------------------------------------------------------------- /BranchTests/BNCTestCase.Test.m: -------------------------------------------------------------------------------- 1 | /** 2 | @file BNCTestCase.Test.m 3 | @package BranchTests 4 | @brief Test cases for the BNCTestCase super class. 5 | 6 | @author Edward Smith 7 | @date April 2018 8 | @copyright Copyright © 2018 Branch. All rights reserved. 9 | */ 10 | 11 | #import "BNCTestCase.h" 12 | 13 | @interface BNCTestCaseTest : BNCTestCase 14 | @end 15 | 16 | @implementation BNCTestCaseTest 17 | 18 | - (void) testFailure { 19 | // Un-comment the next line to test a failure case: 20 | // XCTAssert(NO, @"Testing a test failure!"); 21 | XCTAssertTrue(YES, @"Test passes!"); 22 | NSString * bundleID = [NSBundle mainBundle].bundleIdentifier; 23 | NSLog(@"The test bundleID is '%@'.", bundleID); 24 | } 25 | 26 | - (void) testLoadString { 27 | NSString *string = [self stringFromBundleWithKey:@"BNCTestCaseString"]; 28 | XCTAssertEqualObjects(string, @"Test success!"); 29 | } 30 | 31 | @end 32 | -------------------------------------------------------------------------------- /Examples/TestBed-macOS/TestBed-macOS/APPViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // APPViewController.h 3 | // TestBed-Mac 4 | // 5 | // Created by Edward Smith on 5/15/18. 6 | // Copyright © 2018 Edward Smith. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface APPViewController : NSViewController 12 | + (APPViewController*) loadController; 13 | @property (weak) IBOutlet NSButton *trackingDisabled; 14 | @property (weak) IBOutlet NSButton *limitFacebookTracking; 15 | @property (weak) IBOutlet NSTextField *stateField; 16 | @property (weak) IBOutlet NSTextField *urlField; 17 | @property (weak) IBOutlet NSTextField *errorField; 18 | @property (strong) IBOutlet NSTextView *dataTextView; 19 | @property (strong) IBOutlet NSTextView *requestTextView; 20 | @property (strong) IBOutlet NSTextView *responseTextView; 21 | @property (nonatomic, strong) IBOutlet NSWindow* window; 22 | 23 | - (void) clearUIFields; 24 | - (NSArray *) v2Events; 25 | 26 | @end 27 | -------------------------------------------------------------------------------- /Branch/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | $(MARKETING_VERSION) 19 | CFBundleVersion 20 | 1 21 | NSHumanReadableCopyright 22 | Copyright © 2018 Branch. All rights reserved. 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Examples/TestDeepLinking/TestDeepLinkingTests/TestDeepLinkingTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // TestDeepLinkingTests.m 3 | // TestDeepLinkingTests 4 | // 5 | // Created by Nidhi on 2/3/21. 6 | // 7 | 8 | #import 9 | 10 | @interface TestDeepLinkingTests : XCTestCase 11 | 12 | @end 13 | 14 | @implementation TestDeepLinkingTests 15 | 16 | - (void)setUp { 17 | // Put setup code here. This method is called before the invocation of each test method in the class. 18 | } 19 | 20 | - (void)tearDown { 21 | // Put teardown code here. This method is called after the invocation of each test method in the class. 22 | } 23 | 24 | - (void)testExample { 25 | // This is an example of a functional test case. 26 | // Use XCTAssert and related functions to verify your tests produce the correct results. 27 | } 28 | 29 | - (void)testPerformanceExample { 30 | // This is an example of a performance test case. 31 | [self measureBlock:^{ 32 | // Put the code you want to measure the time of here. 33 | }]; 34 | } 35 | 36 | @end 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Branch 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Examples/TestBed-macOS/TestBed-macOSTests/TestBed-MacTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // TestBed-macOSTests.m 3 | // TestBed-macOSTests 4 | // 5 | // Created by Edward Smith on 5/21/18. 6 | // Copyright © 2018 Branch. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface TestBedmacOSTests : XCTestCase 12 | @end 13 | 14 | @implementation TestBedmacOSTests 15 | 16 | - (void)setUp { 17 | [super setUp]; 18 | // Put setup code here. This method is called before the invocation of each test method in the class. 19 | } 20 | 21 | - (void)tearDown { 22 | // Put teardown code here. This method is called after the invocation of each test method in the class. 23 | [super tearDown]; 24 | } 25 | 26 | - (void)testExample { 27 | NSLog(@"Yo. Bundle: '%@'.", [NSBundle mainBundle].bundleIdentifier); 28 | // This is an example of a functional test case. 29 | // Use XCTAssert and related functions to verify your tests produce the correct results. 30 | } 31 | 32 | - (void)testPerformanceExample { 33 | // This is an example of a performance test case. 34 | [self measureBlock:^{ 35 | // Put the code you want to measure the time of here. 36 | }]; 37 | } 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /BranchTestHost/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "mac", 5 | "scale" : "1x", 6 | "size" : "16x16" 7 | }, 8 | { 9 | "idiom" : "mac", 10 | "scale" : "2x", 11 | "size" : "16x16" 12 | }, 13 | { 14 | "idiom" : "mac", 15 | "scale" : "1x", 16 | "size" : "32x32" 17 | }, 18 | { 19 | "idiom" : "mac", 20 | "scale" : "2x", 21 | "size" : "32x32" 22 | }, 23 | { 24 | "idiom" : "mac", 25 | "scale" : "1x", 26 | "size" : "128x128" 27 | }, 28 | { 29 | "idiom" : "mac", 30 | "scale" : "2x", 31 | "size" : "128x128" 32 | }, 33 | { 34 | "idiom" : "mac", 35 | "scale" : "1x", 36 | "size" : "256x256" 37 | }, 38 | { 39 | "idiom" : "mac", 40 | "scale" : "2x", 41 | "size" : "256x256" 42 | }, 43 | { 44 | "idiom" : "mac", 45 | "scale" : "1x", 46 | "size" : "512x512" 47 | }, 48 | { 49 | "idiom" : "mac", 50 | "scale" : "2x", 51 | "size" : "512x512" 52 | } 53 | ], 54 | "info" : { 55 | "author" : "xcode", 56 | "version" : 1 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /.github/workflows/stale.yml: -------------------------------------------------------------------------------- 1 | # This workflow warns and then closes issues and PRs that have had no activity for a specified amount of time. 2 | # 3 | # You can adjust the behavior by modifying this file. 4 | # For more information, see: 5 | # https://github.com/actions/stale 6 | name: Mark stale issues 7 | 8 | on: 9 | schedule: 10 | - cron: '0 0 * * *' 11 | 12 | jobs: 13 | stale: 14 | 15 | runs-on: ubuntu-latest 16 | permissions: 17 | issues: write 18 | 19 | steps: 20 | - uses: actions/stale@v5 21 | with: 22 | repo-token: ${{ github.token }} 23 | days-before-issue-stale: 60 24 | days-before-close: 7 25 | stale-issue-message: 'This issue has been automatically marked as stale due to inactivity for 60 days. If this issue is still relevant, please respond with any updates or this issue will be closed in 7 days. If you believe this is a mistake, please comment to let us know. Thank you for your contributions.' 26 | stale-issue-label: 'no-issue-activity' 27 | close-issue-message: 'This issue has been closed due to inactivity. If this issue is still relevant, please reopen it or create a new one. Thank you for your contributions.' 28 | start-date: '2023-05-22' 29 | -------------------------------------------------------------------------------- /Examples/TestDeepLinking/TestDeepLinking/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "mac", 5 | "scale" : "1x", 6 | "size" : "16x16" 7 | }, 8 | { 9 | "idiom" : "mac", 10 | "scale" : "2x", 11 | "size" : "16x16" 12 | }, 13 | { 14 | "idiom" : "mac", 15 | "scale" : "1x", 16 | "size" : "32x32" 17 | }, 18 | { 19 | "idiom" : "mac", 20 | "scale" : "2x", 21 | "size" : "32x32" 22 | }, 23 | { 24 | "idiom" : "mac", 25 | "scale" : "1x", 26 | "size" : "128x128" 27 | }, 28 | { 29 | "idiom" : "mac", 30 | "scale" : "2x", 31 | "size" : "128x128" 32 | }, 33 | { 34 | "idiom" : "mac", 35 | "scale" : "1x", 36 | "size" : "256x256" 37 | }, 38 | { 39 | "idiom" : "mac", 40 | "scale" : "2x", 41 | "size" : "256x256" 42 | }, 43 | { 44 | "idiom" : "mac", 45 | "scale" : "1x", 46 | "size" : "512x512" 47 | }, 48 | { 49 | "idiom" : "mac", 50 | "scale" : "2x", 51 | "size" : "512x512" 52 | } 53 | ], 54 | "info" : { 55 | "author" : "xcode", 56 | "version" : 1 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Branch/BNCNetworkInformation.h: -------------------------------------------------------------------------------- 1 | /** 2 | @file BNCNetworkInformation.h 3 | @package Branch 4 | @brief This class retreives information about the local network. 5 | 6 | @author Edward Smith 7 | @date August 2018 8 | @copyright Copyright © 2018 Branch. All rights reserved. 9 | */ 10 | 11 | #import 12 | 13 | NS_ASSUME_NONNULL_BEGIN 14 | 15 | typedef NS_ENUM(NSInteger, BNCInetAddressType) { 16 | BNCInetAddressTypeUnknown = 0, 17 | BNCInetAddressTypeIPv4, 18 | BNCInetAddressTypeIPv6 19 | }; 20 | 21 | @interface BNCNetworkInformation : NSObject 22 | + (BNCNetworkInformation*_Nullable) local; 23 | + (NSArray*) areaEntries; 24 | + (NSArray*) currentInterfaces; 25 | @property (nonatomic, readonly, strong) NSString*interface; 26 | @property (nonatomic, readonly, strong) NSData*_Nullable address; 27 | @property (nonatomic, readonly, strong) NSString*displayAddress; 28 | @property (nonatomic, readonly, strong) NSData*_Nullable inetAddress; 29 | @property (nonatomic, readonly, strong) NSString*displayInetAddress; 30 | @property (nonatomic, readonly, assign) BNCInetAddressType inetAddressType; 31 | @end 32 | 33 | NS_ASSUME_NONNULL_END 34 | 35 | -------------------------------------------------------------------------------- /Branch/NSString+Branch.h: -------------------------------------------------------------------------------- 1 | /** 2 | @file NSString+Branch.h 3 | @package Branch 4 | @brief NSString Additions 5 | 6 | @author Edward Smith 7 | @date February 2017 8 | @copyright Copyright © 2017 Branch. All rights reserved. 9 | */ 10 | 11 | #import "BranchHeader.h" 12 | 13 | NS_ASSUME_NONNULL_BEGIN 14 | 15 | @interface NSString (Branch) 16 | 17 | /** 18 | Compares the receiver to a masked string. Masked characters (the '*' character) are 19 | ignored for purposes of the compare. 20 | 21 | @return YES if string (ignoring any masked characters) is equal to the receiver. 22 | */ 23 | - (BOOL) bnc_isEqualToMaskedString:(NSString*_Nullable)string; 24 | 25 | /** @return Returns a string that is truncated at the first null character. */ 26 | - (NSString*) bnc_stringTruncatedAtNull; 27 | 28 | /** 29 | The `containsString:` method isn't supported pre-iOS 8. Here we roll our own. 30 | 31 | @param string The string to for comparison. 32 | 33 | @return Reurns true if the instance contains the string. 34 | */ 35 | - (BOOL) bnc_containsString:(NSString*_Nullable)string; 36 | @end 37 | 38 | FOUNDATION_EXPORT void BNCForceNSStringCategoryToLoad(void) 39 | __attribute__((constructor)); 40 | 41 | NS_ASSUME_NONNULL_END 42 | -------------------------------------------------------------------------------- /Branch/BNCNetworkService.h: -------------------------------------------------------------------------------- 1 | /** 2 | @file BNCNetworkService.h 3 | @package Branch 4 | @brief Basic Networking Services 5 | 6 | @author Edward Smith 7 | @date April 2017 8 | @copyright Copyright © 2017 Branch. All rights reserved. 9 | */ 10 | 11 | #import "BranchHeader.h" 12 | #import "BranchNetworkServiceProtocol.h" 13 | 14 | NS_ASSUME_NONNULL_BEGIN 15 | 16 | #pragma mark BNCNetworkOperation 17 | 18 | @interface BNCNetworkOperation : NSObject 19 | 20 | @property (readonly) NSMutableURLRequest* request; 21 | @property (readonly) NSError*_Nullable error; 22 | @property (readonly) NSInteger HTTPStatusCode; 23 | @property (readonly) NSData*_Nullable responseData; 24 | 25 | - (void) start; 26 | - (void) cancel; 27 | @end 28 | 29 | #pragma mark - BNCNetworkService 30 | 31 | @interface BNCNetworkService : NSObject 32 | 33 | - (id) networkOperationWithURLRequest:(NSMutableURLRequest*)request 34 | completion:(void (^)(idoperation))completion; 35 | 36 | @property (atomic, assign) NSInteger maxConcurrentOperationCount; 37 | 38 | - (void) cancelAllOperations; 39 | 40 | @end 41 | 42 | NS_ASSUME_NONNULL_END 43 | 44 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Reference 2 | SDK-XXXX -- . 3 | 4 | ## Summary 5 | <!-- Simple summary of what was changed. --> 6 | 7 | ## Motivation 8 | <!-- Why are you making this change? If it's for fixing a bug, if possible, please include a code snippet or example project that demonstrates the issue. --> 9 | 10 | ## Type Of Change 11 | <!-- Please delete options that are not relevant --> 12 | - [ ] Bug fix (non-breaking change which fixes an issue) 13 | - [ ] New feature (non-breaking change which adds functionality) 14 | - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) 15 | - [ ] This change requires a documentation update 16 | 17 | ## Testing Instructions 18 | <!-- Testing instructions, example code snippets, etc --> 19 | 20 | 21 | <!-- Checklist --> 22 | <!-- My code follows the style guidelines of this project --> 23 | <!-- I have performed a self-review of my code --> 24 | <!-- I have commented my code, particularly in hard-to-understand areas --> 25 | <!-- I have made corresponding changes to the documentation --> 26 | <!-- I have added tests that prove my fix is effective or that my feature works --> 27 | <!-- New and existing unit tests pass locally with my changes --> 28 | 29 | cc @BranchMetrics/saas-sdk-devs for visibility. 30 | -------------------------------------------------------------------------------- /Examples/TestBed-macOS/TestBed-macOS/APPActionItemView.m: -------------------------------------------------------------------------------- 1 | // 2 | // APPActionItemView.m 3 | // TestBed-Mac 4 | // 5 | // Created by Edward on 5/30/18. 6 | // Copyright © 2018 Branch. All rights reserved. 7 | // 8 | 9 | #import "APPActionItemView.h" 10 | 11 | @interface APPActionItemSubview : NSView 12 | @property (assign) BOOL selected; 13 | @end 14 | 15 | @implementation APPActionItemSubview 16 | 17 | - (void)drawRect:(NSRect)dirtyRect { 18 | if (self.selected) { 19 | [[NSColor selectedTextBackgroundColor] set]; 20 | NSRectFill([self bounds]); 21 | } 22 | [super drawRect:dirtyRect]; 23 | } 24 | 25 | @end 26 | 27 | #pragma mark - APPActionItemView 28 | 29 | @implementation APPActionItemView 30 | 31 | - (void)setHighlightState:(NSCollectionViewItemHighlightState)highlightState_ { 32 | [super setHighlightState:highlightState_]; 33 | BOOL selected_ = 34 | (highlightState_ == NSCollectionViewItemHighlightAsDropTarget || 35 | highlightState_ == NSCollectionViewItemHighlightForSelection); 36 | [(APPActionItemSubview*)[self view] setSelected:selected_]; 37 | [(APPActionItemSubview*)[self view] setNeedsDisplay:YES]; 38 | } 39 | 40 | - (NSCollectionViewItemHighlightState) highlightState { 41 | return [super highlightState]; 42 | } 43 | 44 | @end 45 | -------------------------------------------------------------------------------- /BranchTestHost/Info.plist: -------------------------------------------------------------------------------- 1 | <?xml version="1.0" encoding="UTF-8"?> 2 | <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> 3 | <plist version="1.0"> 4 | <dict> 5 | <key>CFBundleDevelopmentRegion</key> 6 | <string>$(DEVELOPMENT_LANGUAGE)</string> 7 | <key>CFBundleExecutable</key> 8 | <string>$(EXECUTABLE_NAME)</string> 9 | <key>CFBundleIconFile</key> 10 | <string></string> 11 | <key>CFBundleIdentifier</key> 12 | <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string> 13 | <key>CFBundleInfoDictionaryVersion</key> 14 | <string>6.0</string> 15 | <key>CFBundleName</key> 16 | <string>$(PRODUCT_NAME)</string> 17 | <key>CFBundlePackageType</key> 18 | <string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string> 19 | <key>CFBundleShortVersionString</key> 20 | <string>1.0</string> 21 | <key>CFBundleVersion</key> 22 | <string>1</string> 23 | <key>LSMinimumSystemVersion</key> 24 | <string>$(MACOSX_DEPLOYMENT_TARGET)</string> 25 | <key>NSHumanReadableCopyright</key> 26 | <string>Copyright © 2020 Branch. All rights reserved.</string> 27 | <key>NSMainStoryboardFile</key> 28 | <string>Main</string> 29 | <key>NSPrincipalClass</key> 30 | <string>NSApplication</string> 31 | <key>NSSupportsAutomaticTermination</key> 32 | <true/> 33 | <key>NSSupportsSuddenTermination</key> 34 | <true/> 35 | </dict> 36 | </plist> 37 | -------------------------------------------------------------------------------- /Examples/TestBed-macOS/TestBed-macOSUITests/TestBedUITest.h: -------------------------------------------------------------------------------- 1 | // 2 | // TestBedUITest.h 3 | // TestBed-macOS 4 | // 5 | // Created by Nidhi on 11/3/20. 6 | // Copyright © 2020 Branch. All rights reserved. 7 | // 8 | 9 | #ifndef TestBedUITest_h 10 | #define TestBedUITest_h 11 | 12 | #import <XCTest/XCTest.h> 13 | 14 | #define TRACKING_STATE_UNKNOWN -1 15 | #define TRACKING_ENABLED 0 16 | #define TRACKING_DISABLED 1 17 | 18 | @interface TestBedUITest : XCTestCase 19 | 20 | @property BOOL appLaunched; 21 | @property NSInteger trackingState; 22 | 23 | - (XCTWaiterResult) launchAppAndWaitForSessionStart; 24 | - (NSString *) serverRequestString; 25 | - (NSString *) serverResponseString; 26 | - (void) setIdentity; 27 | - (void) logOut; 28 | - (NSString *) createShortLink; 29 | - (void) openLastLink; 30 | - (NSString *) getErrorString; 31 | - (void) logEvent:(NSString *)eventName; 32 | - (void) logAllEvents; 33 | - (NSString *) dataTextViewString; 34 | -(void) enableTracking; 35 | -(void) disableTracking; 36 | -(void) terminateTestBed; 37 | -(NSString *) webPageURLWithRedirection:(BOOL)enabled; 38 | - (void) validateDeepLinkDataForRedirectionEnabled:(bool)enabled; 39 | - (void) takeScreenShot; 40 | @end 41 | 42 | #endif /* TestBedUITest_h */ 43 | 44 | // Adding this message for testing integration on commit from remote machine. 45 | -------------------------------------------------------------------------------- /BranchTests/BNCPersistence.Test.m: -------------------------------------------------------------------------------- 1 | /** 2 | @file BNCPersistenceTest.m 3 | @package BranchTests 4 | @brief Tests for BNCPersistence. 5 | 6 | @author Edward Smith 7 | @date May 2018 8 | @copyright Copyright © 2018 Branch. All rights reserved. 9 | */ 10 | 11 | #import "BNCTestCase.h" 12 | #import "BNCPersistence.h" 13 | 14 | @interface BNCPersistenceTest : BNCTestCase 15 | @end 16 | 17 | @implementation BNCPersistenceTest 18 | 19 | - (void) testBranchDirectory { 20 | NSURL* url = BNCURLForBranchDataDirectory(); 21 | XCTAssertNotNil(url); 22 | } 23 | 24 | - (void) testSaveLoadRemove { 25 | BNCPersistence*persistence = [[BNCPersistence alloc] initWithAppGroup:@"io.branch.sdk.unit.tests"]; 26 | [persistence removeDataNamed:@"io.branch.sdk.test"]; 27 | 28 | NSString*s = @"Howdy!"; 29 | NSData*sd = [s dataUsingEncoding:NSUTF8StringEncoding]; 30 | NSError*error = [persistence saveDataNamed:@"io.branch.sdk.test" data:sd]; 31 | XCTAssertNil(error); 32 | 33 | NSData*td = [persistence loadDataNamed:@"io.branch.sdk.test"]; 34 | XCTAssertEqualObjects(sd, td); 35 | 36 | error = [persistence removeDataNamed:@"io.branch.sdk.test"]; 37 | XCTAssertNil(error); 38 | 39 | td = [persistence loadDataNamed:@"io.branch.sdk.test"]; 40 | XCTAssertNil(td); 41 | } 42 | 43 | @end 44 | -------------------------------------------------------------------------------- /Branch/BNCLocalization.h: -------------------------------------------------------------------------------- 1 | /** 2 | @file BNCLocalization.h 3 | @package Branch 4 | @brief Branch string localizations. 5 | 6 | @author Parth Kalavadia 7 | @date July 2017 8 | @copyright Copyright © 2017 Branch. All rights reserved. 9 | */ 10 | 11 | #import "BranchHeader.h" 12 | 13 | NS_ASSUME_NONNULL_BEGIN 14 | 15 | /** 16 | Since the Branch SDK can be shipped as a static library, it can't use the standard Apple string 17 | localization mechanism. 18 | 19 | Use this class to localize the few user-facing string resources the Branch SDK has. 20 | */ 21 | @interface BNCLocalization : NSObject 22 | 23 | + (instancetype) shared; 24 | + (NSString*) applicationLanguage; 25 | + (NSDictionary<NSString*, NSDictionary*>*) languageDictionaries; 26 | - (NSString*) localizeString:(NSString*)string; 27 | 28 | /// Set to an empty string or nil to reset the current language. 29 | @property (copy, atomic) NSString* currentLanguage; 30 | @property (strong, atomic, readonly) NSDictionary *currentLanguageDictionary; 31 | @end 32 | 33 | #pragma mark Convenience Functions 34 | 35 | static inline NSString* BNCLocalizedString(NSString*const string) { 36 | return [[BNCLocalization shared] localizeString:string]; 37 | } 38 | 39 | extern NSString* BNCLocalizedFormattedString(NSString*const format, ...) NS_FORMAT_FUNCTION(1,2); 40 | 41 | NS_ASSUME_NONNULL_END 42 | -------------------------------------------------------------------------------- /BranchTests/BNCNetworkInformation.Test.m: -------------------------------------------------------------------------------- 1 | /** 2 | @file BNCNetworkInformation.Test.m 3 | @package BranchTests 4 | @brief Tests for BNCNetworkInformation. 5 | 6 | @author Edward Smith 7 | @date August 2018 8 | @copyright Copyright © 2018 Branch. All rights reserved. 9 | */ 10 | 11 | #import "BNCTestCase.h" 12 | #import "BNCNetworkInformation.h" 13 | 14 | @interface BNCNetworkInformationTest : BNCTestCase 15 | @end 16 | 17 | @implementation BNCNetworkInformationTest 18 | 19 | - (void)testAreaEntries { 20 | NSArray*entries = [BNCNetworkInformation areaEntries]; 21 | XCTAssertGreaterThan(entries.count, 0); 22 | NSLog(@"%@", entries); 23 | } 24 | 25 | - (void)testCurrentInterfaces { 26 | NSArray*entries = [BNCNetworkInformation currentInterfaces]; 27 | XCTAssertGreaterThan(entries.count, 0); 28 | NSLog(@"%@", entries); 29 | } 30 | 31 | - (void)testLocal { 32 | BNCNetworkInformation*entry = [BNCNetworkInformation local]; 33 | XCTAssertGreaterThan(entry.interface.length, 0); 34 | XCTAssertGreaterThan(entry.address.length, 0); 35 | XCTAssertGreaterThan(entry.displayAddress.length, 0); 36 | XCTAssertGreaterThan(entry.inetAddress.length, 0); 37 | XCTAssertGreaterThan(entry.displayInetAddress.length, 0); 38 | XCTAssertGreaterThan(entry.inetAddressType, 0); 39 | NSLog(@"%@", entry); 40 | } 41 | 42 | @end 43 | -------------------------------------------------------------------------------- /Examples/TestBed-macOS/TestPlan.xctestplan: -------------------------------------------------------------------------------- 1 | { 2 | "configurations" : [ 3 | { 4 | "id" : "733BCF4B-C794-434C-9A91-D0889364C340", 5 | "name" : "Configuration 1", 6 | "options" : { 7 | 8 | } 9 | } 10 | ], 11 | "defaultOptions" : { 12 | 13 | }, 14 | "testTargets" : [ 15 | { 16 | "enabled" : false, 17 | "target" : { 18 | "containerPath" : "container:..\/..\/Branch.xcodeproj", 19 | "identifier" : "5F78ED8E249C317400E313B9", 20 | "name" : "BranchTests" 21 | } 22 | }, 23 | { 24 | "skippedTests" : [ 25 | "BranchSDKUITestCase", 26 | "OpenURL_macOSUITests", 27 | "SafariOpenLink", 28 | "SafariOpenLinkUITests", 29 | "TestBedUIChromeTest", 30 | "TestBedUIDeepLinkDataTest\/testDeepLink", 31 | "TestBedUIRaceCondTest\/testChromeAllowAppLaunch", 32 | "TestBedUIRaceCondTest\/testChromeCancelAppLaunch", 33 | "TestBedUISafariTest\/test1OpenURLInSafariInNewTab", 34 | "TestBedUISafariTest\/testOpenURLInSafariInNewWindow", 35 | "TestBedUISafariTest\/testOpenURLInSafariInPrivateWindow" 36 | ], 37 | "target" : { 38 | "containerPath" : "container:TestBed-macOS.xcodeproj", 39 | "identifier" : "0310053A253A4BE500E135E0", 40 | "name" : "TestBed-macOSUITests" 41 | } 42 | } 43 | ], 44 | "version" : 1 45 | } 46 | -------------------------------------------------------------------------------- /Examples/TestDeepLinking/TestDeepLinking/Info.plist: -------------------------------------------------------------------------------- 1 | <?xml version="1.0" encoding="UTF-8"?> 2 | <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> 3 | <plist version="1.0"> 4 | <dict> 5 | <key>CFBundleDevelopmentRegion</key> 6 | <string>$(DEVELOPMENT_LANGUAGE)</string> 7 | <key>CFBundleExecutable</key> 8 | <string>$(EXECUTABLE_NAME)</string> 9 | <key>CFBundleIconFile</key> 10 | <string></string> 11 | <key>CFBundleIdentifier</key> 12 | <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string> 13 | <key>CFBundleInfoDictionaryVersion</key> 14 | <string>6.0</string> 15 | <key>CFBundleName</key> 16 | <string>$(PRODUCT_NAME)</string> 17 | <key>CFBundlePackageType</key> 18 | <string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string> 19 | <key>CFBundleShortVersionString</key> 20 | <string>1.0</string> 21 | <key>CFBundleURLTypes</key> 22 | <array> 23 | <dict> 24 | <key>CFBundleTypeRole</key> 25 | <string>Editor</string> 26 | <key>CFBundleURLSchemes</key> 27 | <array> 28 | <string>testDeepLinking-Mac</string> 29 | </array> 30 | </dict> 31 | </array> 32 | <key>CFBundleVersion</key> 33 | <string>1</string> 34 | <key>LSMinimumSystemVersion</key> 35 | <string>$(MACOSX_DEPLOYMENT_TARGET)</string> 36 | <key>NSMainNibFile</key> 37 | <string>MainMenu</string> 38 | <key>NSPrincipalClass</key> 39 | <string>NSApplication</string> 40 | </dict> 41 | </plist> 42 | -------------------------------------------------------------------------------- /Branch/BranchLinkProperties.h: -------------------------------------------------------------------------------- 1 | /** 2 | @file BranchLinkProperties.h 3 | @package Branch 4 | @brief Branch link properties: non-content properties that are associated with a link. 5 | 6 | @author Derrick Staten 7 | @date October 2015 8 | @copyright Copyright © 2015 Branch. All rights reserved. 9 | */ 10 | 11 | #import "BranchHeader.h" 12 | 13 | NS_ASSUME_NONNULL_BEGIN 14 | 15 | #ifndef BranchLinkProperties_h 16 | #define BranchLinkProperties_h 17 | 18 | typedef NS_ENUM(NSInteger, BranchLinkType) { 19 | BranchLinkTypeUnlimitedUse = 0, 20 | BranchLinkTypeOneTimeUse = 1 21 | }; 22 | 23 | @interface BranchLinkProperties : NSObject 24 | 25 | + (instancetype)linkPropertiesWithDictionary:(NSDictionary*)dictionary; 26 | - (NSDictionary*) dictionary; 27 | 28 | @property (nonatomic, strong) NSArray<NSString*>*_Nullable tags; 29 | @property (nonatomic, strong) NSString*_Nullable feature; 30 | @property (nonatomic, strong) NSString*_Nullable alias; 31 | @property (nonatomic, strong) NSString*_Nullable channel; 32 | @property (nonatomic, strong) NSString*_Nullable stage; 33 | @property (nonatomic, strong) NSString*_Nullable campaign; 34 | @property (nonatomic, assign) NSInteger matchDuration; 35 | @property (nonatomic, assign) BranchLinkType linkType; 36 | @property (nonatomic, strong, null_resettable) NSMutableDictionary* controlParams; 37 | @end 38 | 39 | #endif 40 | 41 | NS_ASSUME_NONNULL_END 42 | -------------------------------------------------------------------------------- /Branch/BNCPersistence.h: -------------------------------------------------------------------------------- 1 | /** 2 | @file BNCPersistence.h 3 | @package Branch 4 | @brief Persists a smallish (< 1mb?) set of data between app runs. 5 | 6 | @author Edward Smith 7 | @date May 2018 8 | @copyright Copyright © 2018 Branch. All rights reserved. 9 | */ 10 | 11 | #import "BranchHeader.h" 12 | 13 | NS_ASSUME_NONNULL_BEGIN 14 | 15 | /** 16 | Returns a URL appropriate for storing persistent settings that aren't normally user visible. 17 | 18 | @discussion This URL is defined as a function so it can be called before the class system are loaded and 19 | initialized. 20 | 21 | @return Returns a file system URL. 22 | */ 23 | NSURL* BNCURLForBranchDataDirectory(void); 24 | 25 | #pragma mark - BNCPersistence 26 | 27 | /** 28 | A generalized but very basic low-volume persistent data store. 29 | */ 30 | @interface BNCPersistence : NSObject 31 | 32 | + (instancetype) new NS_UNAVAILABLE; 33 | - (instancetype) init NS_UNAVAILABLE; 34 | 35 | - (instancetype) initWithAppGroup:(NSString*)appGroup NS_DESIGNATED_INITIALIZER; 36 | 37 | - (NSData*_Nullable) loadDataNamed:(NSString*)name; 38 | - (NSError*_Nullable) saveDataNamed:(NSString*)name data:(NSData*)data; 39 | - (NSError*_Nullable) removeDataNamed:(NSString*)name; 40 | 41 | - (id _Nullable) unarchiveObjectNamed:(NSString*)name; 42 | - (NSError*_Nullable) archiveObject:(id<NSSecureCoding>)object named:(NSString*)name; 43 | @end 44 | 45 | NS_ASSUME_NONNULL_END 46 | -------------------------------------------------------------------------------- /Branch/NSString+Branch.m: -------------------------------------------------------------------------------- 1 | /** 2 | @file NSString+Branch.m 3 | @package Branch 4 | @brief NSString Additions 5 | 6 | @author Edward Smith 7 | @date February 2017 8 | @copyright Copyright © 2017 Branch. All rights reserved. 9 | */ 10 | 11 | #import "NSString+Branch.h" 12 | 13 | __attribute__((constructor)) void BNCForceNSStringCategoryToLoad() { 14 | // Nothing here, but forces linker to load the category. 15 | } 16 | 17 | @implementation NSString (Branch) 18 | 19 | - (BOOL) bnc_isEqualToMaskedString:(NSString*_Nullable)string { 20 | // Un-comment for debugging: 21 | // NSLog(@"bnc_isEqualToMaskedString self/string:\n%@\n%@.", self, string); 22 | if (!string) return NO; 23 | if (self.length != string.length) return NO; 24 | for (NSUInteger idx = 0; idx < self.length; idx++) { 25 | unichar p = [self characterAtIndex:idx]; 26 | unichar q = [string characterAtIndex:idx]; 27 | if (q != '*' && p != q) return NO; 28 | } 29 | return YES; 30 | } 31 | 32 | - (NSString*_Nonnull) bnc_stringTruncatedAtNull { 33 | NSRange range = [self rangeOfString:@"\0"]; 34 | if (range.location == NSNotFound) 35 | return self; 36 | range.length = range.location; 37 | range.location = 0; 38 | return [self substringWithRange:range]; 39 | } 40 | 41 | - (BOOL) bnc_containsString:(NSString*_Nullable)string { 42 | return (string && [self rangeOfString:(NSString*_Nonnull)string].location != NSNotFound); 43 | } 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /Scripts/deploy-preflight: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euo pipefail 3 | 4 | # deploy-preflight - Check that the development environment is set up for deployment. 5 | # 6 | # Edward Smith, January 2017 7 | 8 | 9 | wasError=0 10 | 11 | 12 | function checkApp() { 13 | local appname="$1" 14 | local apppath=$(./scripts/whichapp "$appname" 2>&1) 15 | if (( ${#apppath} == 0 )); then 16 | echo ">>> Error: The application $appname not installed." 1>&2 17 | wasError=1 18 | fi 19 | } 20 | 21 | 22 | function checkTool() { 23 | local appname="$1" 24 | local apppath=$(which "$appname") 25 | if (( ${#apppath} == 0 )); then 26 | echo ">>> Error: The tool '$appname' is not installed." 1>&2 27 | wasError=1 28 | fi 29 | } 30 | 31 | 32 | function checkVariable() { 33 | local v=$1 34 | if [ -z ${!v+x} ]; then 35 | echo ">>> Error: Bash variable '$v' is unset." 1>&2 36 | wasError=1 37 | fi 38 | local vval=${!v} 39 | if (( ${#vval} == 0 )); then 40 | echo ">>> Error: Bash variable '$v' is empty." 1>&2 41 | wasError=1 42 | fi 43 | } 44 | 45 | # Check Xcode 46 | checkApp Xcode 47 | 48 | # Check CocoaPods 49 | checkTool pod 50 | 51 | # Check Carthage 52 | checkTool carthage 53 | 54 | # Check Github 55 | if ! git push --dry-run &> /dev/null; then 56 | echo ">>> Error: Not able to push to github." 1>&2 57 | wasError=1 58 | fi 59 | 60 | if [[ $wasError != 0 ]]; then 61 | echo ">>> Error: deploy-preflight failed." 1>&2 62 | exit 1 63 | fi 64 | 65 | -------------------------------------------------------------------------------- /Examples/TestBed-macOS/TestBed-macOSUITests/TestBedUIV2EventTest.m: -------------------------------------------------------------------------------- 1 | // 2 | // TestBedUIV2EventTest.m 3 | // TestBed-macOSUITests 4 | // 5 | // Created by Nidhi on 11/6/20. 6 | // Copyright © 2020 Branch. All rights reserved. 7 | // 8 | 9 | 10 | #import "TestBedUITest.h" 11 | #import "TestBedUIUtils.h" 12 | #import <Branch/BranchEvent.h> 13 | 14 | @interface TestBedUIV2EventTest : TestBedUITest 15 | 16 | @end 17 | 18 | @implementation TestBedUIV2EventTest 19 | 20 | - (void)setUp { 21 | [super setUp]; 22 | } 23 | 24 | - (void)tearDown { 25 | [super tearDown]; 26 | } 27 | 28 | - (void)testSendV2Events { 29 | 30 | XCTWaiterResult result = [self launchAppAndWaitForSessionStart]; 31 | 32 | if (result == XCTWaiterResultCompleted) { 33 | 34 | if (self.trackingState == TRACKING_DISABLED) { 35 | [self enableTracking]; 36 | } 37 | 38 | NSArray *events = [BranchEvent standardEvents]; 39 | 40 | for (NSString *eventName in events) { 41 | 42 | [self logEvent:eventName]; 43 | XCTAssertTrue([[self serverRequestString] containsString:@"/v2/event/standard"]); 44 | XCTAssertTrue([[self getErrorString] isEqualToString:@"< None >"]); 45 | } 46 | 47 | [self logEvent:@"Custom Event"]; 48 | XCTAssertTrue([[self serverRequestString] containsString:@"/v2/event/custom"]); 49 | XCTAssertTrue([[self getErrorString] isEqualToString:@"< None >"]); 50 | 51 | } else { 52 | XCTFail("App Launch / Session Start Failed."); 53 | } 54 | } 55 | 56 | @end 57 | -------------------------------------------------------------------------------- /Branch/BNCURLBlackList.h: -------------------------------------------------------------------------------- 1 | /** 2 | @file BNCURLBlackList.h 3 | @package Branch 4 | @brief Manages a list of sensitive URLs such as login data that should not be handled by Branch. 5 | 6 | @author Edward Smith 7 | @date February 14, 2018 8 | @copyright Copyright © 2018 Branch. All rights reserved. 9 | */ 10 | 11 | #import "BranchHeader.h" 12 | @class Branch; 13 | 14 | NS_ASSUME_NONNULL_BEGIN 15 | 16 | // TODO: rename this class 17 | @interface BNCURLBlackList : NSObject 18 | 19 | - (instancetype) initWithBlackList:(NSArray<NSString*>*)blacklist_ version:(NSInteger)version_ 20 | NS_DESIGNATED_INITIALIZER; 21 | 22 | /** 23 | @brief Checks if a given URL should be ignored (blacklisted). 24 | 25 | @param url The URL to be checked. 26 | @return Returns true if the provided URL should be ignored. 27 | */ 28 | - (BOOL) isBlackListedURL:(NSURL*_Nullable)url; 29 | 30 | /** 31 | @brief Returns the pattern that matches a URL, if any. 32 | 33 | @param url The URL to be checked. 34 | @return Returns the pattern matching the URL or `nil` if no patterns match. 35 | */ 36 | - (NSString*_Nullable) blackListPatternMatchingURL:(NSURL*_Nullable)url; 37 | 38 | /// Refreshes the list of ignored URLs from the server. 39 | - (void) refreshBlackListFromServerWithBranch:(Branch*)branch 40 | completion:(void (^_Nullable) (BNCURLBlackList*blackList, NSError*_Nullable error))completion; 41 | 42 | @property (assign) NSInteger blackListVersion; 43 | @property (strong) NSArray<NSString*>*_Nullable blackList; 44 | @end 45 | 46 | NS_ASSUME_NONNULL_END 47 | 48 | -------------------------------------------------------------------------------- /BranchMacOS.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |spec| 2 | spec.name = "BranchMacOS" 3 | spec.version = "1.5.0" 4 | spec.summary = "Create an HTTP URL for any piece of content in your MacOS app" 5 | spec.description = <<-DESC 6 | - Want the highest possible conversions on your sharing feature? 7 | - Want to measure the k-factor of your invite feature? 8 | - Want a whole referral program in 10 lines of code, with automatic user-user attribution and rewarding? 9 | - Want to pass data (deep link) from a URL across install and open? 10 | - Want custom onboarding post install? 11 | 12 | Use the Branch SDK (branch.io) to create and power the links that point back to your apps for all of these things and more. Branch makes it incredibly simple to create powerful deep links that can pass data across app install and open while handling all edge cases (using on desktop vs. mobile vs. already having the app installed, etc). Best of all, it's really simple to start using the links for your own app: only 2 lines of code to register the deep link router and one more line of code to create the links with custom data. 13 | DESC 14 | spec.homepage = "https://help.branch.io/developers-hub/docs/mac-os-sdk-overview" 15 | spec.license = "MIT" 16 | spec.author = { "Branch" => "support@branch.io" } 17 | spec.source = { git: "https://github.com/BranchMetrics/mac-branch-deep-linking.git", tag: spec.version.to_s } 18 | spec.osx.deployment_target = "10.15" 19 | spec.source_files = "Branch/*.{h,m}" 20 | spec.frameworks = "WebKit" , "AdSupport" 21 | spec.header_dir = 'Branch' 22 | end 23 | -------------------------------------------------------------------------------- /Branch/BNCQRCodeCache.m: -------------------------------------------------------------------------------- 1 | // 2 | // BNCQRCodeCache.m 3 | // BranchMacOS 4 | // 5 | // Created by Nipun Singh on 5/24/22. 6 | // Copyright © 2022 Branch, Inc. All rights reserved. 7 | // 8 | 9 | #import "BNCQRCodeCache.h" 10 | 11 | @interface BNCQRCodeCache() 12 | @property (nonatomic, strong) NSMutableDictionary *cache; 13 | @end 14 | 15 | @implementation BNCQRCodeCache 16 | 17 | //Can only hold one QR code in cache. Just used to debounce. 18 | + (BNCQRCodeCache *) sharedInstance { 19 | static BNCQRCodeCache *singleton; 20 | static dispatch_once_t onceToken; 21 | dispatch_once(&onceToken, ^{ 22 | singleton = [BNCQRCodeCache new]; 23 | }); 24 | return singleton; 25 | } 26 | 27 | - (instancetype)init { 28 | if ((self = [super init])) { 29 | self.cache = [[NSMutableDictionary alloc] init]; 30 | } 31 | return self; 32 | } 33 | 34 | - (void)addQRCodeToCache:(NSData *)qrCodeData withParams:(NSMutableDictionary *)parameters { 35 | @synchronized (self) { 36 | [self.cache removeAllObjects]; 37 | NSMutableDictionary *tempParams = [parameters mutableCopy]; 38 | [tempParams[@"data"] removeObjectForKey:@"$creation_timestamp"]; 39 | self.cache[tempParams] = qrCodeData; 40 | } 41 | } 42 | 43 | - (NSData *)checkQRCodeCache:(NSMutableDictionary *)parameters { 44 | NSData *qrCode; 45 | @synchronized (self) { 46 | NSMutableDictionary *tempParams = [parameters mutableCopy]; 47 | [tempParams[@"data"] removeObjectForKey:@"$creation_timestamp"]; 48 | qrCode = self.cache[tempParams]; 49 | } 50 | return qrCode; 51 | } 52 | 53 | @end 54 | -------------------------------------------------------------------------------- /Examples/TestBed-macOS/TestBed-macOS/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "16x16", 5 | "idiom" : "mac", 6 | "filename" : "AppIcon-16.png", 7 | "scale" : "1x" 8 | }, 9 | { 10 | "size" : "16x16", 11 | "idiom" : "mac", 12 | "filename" : "AppIcon-16@2x.png", 13 | "scale" : "2x" 14 | }, 15 | { 16 | "size" : "32x32", 17 | "idiom" : "mac", 18 | "filename" : "AppIcon-32.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "32x32", 23 | "idiom" : "mac", 24 | "filename" : "AppIcon-32@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "128x128", 29 | "idiom" : "mac", 30 | "filename" : "AppIcon-128.png", 31 | "scale" : "1x" 32 | }, 33 | { 34 | "size" : "128x128", 35 | "idiom" : "mac", 36 | "filename" : "AppIcon-128@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "256x256", 41 | "idiom" : "mac", 42 | "filename" : "AppIcon-256.png", 43 | "scale" : "1x" 44 | }, 45 | { 46 | "size" : "256x256", 47 | "idiom" : "mac", 48 | "filename" : "AppIcon-256@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "512x512", 53 | "idiom" : "mac", 54 | "filename" : "AppIcon-512.png", 55 | "scale" : "1x" 56 | }, 57 | { 58 | "size" : "512x512", 59 | "idiom" : "mac", 60 | "filename" : "AppIcon-512@2x.png", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } -------------------------------------------------------------------------------- /Examples/TestDeepLinking/TestDeepLinkingUITests/TestDeepLinkingUITests.m: -------------------------------------------------------------------------------- 1 | // 2 | // TestDeepLinkingUITests.m 3 | // TestDeepLinkingUITests 4 | // 5 | // Created by Nidhi on 2/3/21. 6 | // 7 | 8 | #import <XCTest/XCTest.h> 9 | 10 | @interface TestDeepLinkingUITests : XCTestCase 11 | 12 | @end 13 | 14 | @implementation TestDeepLinkingUITests 15 | 16 | - (void)setUp { 17 | // Put setup code here. This method is called before the invocation of each test method in the class. 18 | 19 | // In UI tests it is usually best to stop immediately when a failure occurs. 20 | self.continueAfterFailure = NO; 21 | 22 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. 23 | } 24 | 25 | - (void)tearDown { 26 | // Put teardown code here. This method is called after the invocation of each test method in the class. 27 | } 28 | 29 | - (void)testExample { 30 | // UI tests must launch the application that they test. 31 | XCUIApplication *app = [[XCUIApplication alloc] init]; 32 | [app launch]; 33 | 34 | // Use recording to get started writing UI tests. 35 | // Use XCTAssert and related functions to verify your tests produce the correct results. 36 | } 37 | 38 | - (void)testLaunchPerformance { 39 | if (@available(macOS 10.15, iOS 13.0, tvOS 13.0, *)) { 40 | // This measures how long it takes to launch your application. 41 | [self measureWithMetrics:@[[[XCTApplicationLaunchMetric alloc] init]] block:^{ 42 | [[[XCUIApplication alloc] init] launch]; 43 | }]; 44 | } 45 | } 46 | 47 | @end 48 | -------------------------------------------------------------------------------- /BranchTests/BNCTestCase.h: -------------------------------------------------------------------------------- 1 | /** 2 | @file BNCTestCase.h 3 | @package BranchTests 4 | @brief The Branch testing framework super class. 5 | 6 | @author Edward Smith 7 | @date April 2017 8 | @copyright Copyright © 2017 Branch. All rights reserved. 9 | */ 10 | 11 | #import <XCTest/XCTest.h> 12 | #import "NSString+Branch.h" 13 | #import "BNCThreads.h" 14 | #import "BranchMainClass+Private.h" 15 | #import "BNCTestNetworkService.h" 16 | #import "BNCDevice.h" 17 | #import "BranchMainClass.h" 18 | 19 | FOUNDATION_EXPORT NSString*_Nonnull const BNCTestBranchKey; 20 | 21 | #define BNCTAssertEqualMaskedString(string, mask) { \ 22 | if ((id)string != nil && (id)mask != nil && [string bnc_isEqualToMaskedString:mask]) { \ 23 | } else { \ 24 | XCTAssertEqualObjects(string, mask); \ 25 | } \ 26 | } 27 | 28 | NS_ASSUME_NONNULL_BEGIN 29 | 30 | extern BOOL BNCTestStringMatchesRegex(NSString *string, NSString *regex); 31 | 32 | #define XCTAssertStringMatchesRegex(string, regex) \ 33 | XCTAssertTrue(BNCTestStringMatchesRegex(string, regex)) 34 | 35 | @interface BNCTestCase : XCTestCase 36 | 37 | - (void)safelyFulfillExpectation:(XCTestExpectation *)expectation; 38 | - (void)awaitExpectations; 39 | - (void)resetExpectations; 40 | //- (id)stringMatchingPattern:(NSString *)pattern; 41 | 42 | // Load Resources from the test bundle: 43 | 44 | - (NSString*_Nullable)stringFromBundleWithKey:(NSString*)key; 45 | - (NSString*_Nullable)stringFromBundleJSONWithKey:(NSString *)key; 46 | - (NSMutableDictionary*_Nullable)mutableDictionaryFromBundleJSONWithKey:(NSString*)key; 47 | 48 | - (BOOL) testDeviceSupportsIDFA; 49 | + (BOOL) breakpointsAreEnabledInTests; 50 | @end 51 | 52 | NS_ASSUME_NONNULL_END 53 | -------------------------------------------------------------------------------- /Branch/BranchSession.h: -------------------------------------------------------------------------------- 1 | /** 2 | @file BranchSession.h 3 | @package Branch 4 | @brief Attributes of the current Branch session. 5 | 6 | @author Edward Smith 7 | @date May 2018 8 | @copyright Copyright © 2018 Branch. All rights reserved. 9 | */ 10 | 11 | #import "BranchHeader.h" 12 | #import "BranchLinkProperties.h" 13 | #import "BranchUniversalObject.h" 14 | 15 | NS_ASSUME_NONNULL_BEGIN 16 | 17 | 18 | #ifndef BranchSession_h 19 | #define BranchSession_h 20 | /** 21 | Branch session parameters. 22 | */ 23 | @interface BranchSession : NSObject 24 | 25 | + (instancetype) sessionWithDictionary:(NSDictionary*)dictionary; 26 | 27 | @property (nonatomic, strong) NSString*_Nullable sessionID; 28 | @property (nonatomic, assign) BOOL isFirstSession; 29 | @property (nonatomic, assign) BOOL isBranchURL; 30 | @property (nonatomic, assign, readonly) BOOL matchGuaranteed; 31 | @property (nonatomic, strong, readonly) NSDate* clickTimestamp; 32 | @property (nonatomic, strong) NSURL*_Nullable referringURL; 33 | @property (nonatomic, strong) NSString*_Nullable deviceFingerprintID; 34 | @property (nonatomic, strong) NSString*_Nullable randomizedDeviceToken; 35 | @property (nonatomic, strong) NSString*_Nullable identityID; 36 | @property (nonatomic, strong) NSString*_Nullable randomizedBundleToken; 37 | @property (nonatomic, strong) NSString*_Nullable userIdentityForDeveloper; 38 | @property (nonatomic, strong) NSString*_Nullable linkCreationURL; 39 | @property (nonatomic, strong) BranchUniversalObject*_Nullable linkContent; 40 | @property (nonatomic, strong) BranchLinkProperties*_Nullable linkProperties; 41 | @property (nonatomic, strong) NSDictionary*_Nullable data; 42 | @end 43 | 44 | #endif 45 | 46 | NS_ASSUME_NONNULL_END 47 | -------------------------------------------------------------------------------- /Examples/TestBed-macOS/TestBed-macOS/Info.plist: -------------------------------------------------------------------------------- 1 | <?xml version="1.0" encoding="UTF-8"?> 2 | <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> 3 | <plist version="1.0"> 4 | <dict> 5 | <key>CFBundleDevelopmentRegion</key> 6 | <string>$(DEVELOPMENT_LANGUAGE)</string> 7 | <key>CFBundleExecutable</key> 8 | <string>$(EXECUTABLE_NAME)</string> 9 | <key>CFBundleIconFile</key> 10 | <string></string> 11 | <key>CFBundleIdentifier</key> 12 | <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string> 13 | <key>CFBundleInfoDictionaryVersion</key> 14 | <string>6.0</string> 15 | <key>CFBundleName</key> 16 | <string>$(PRODUCT_NAME)</string> 17 | <key>CFBundlePackageType</key> 18 | <string>APPL</string> 19 | <key>CFBundleShortVersionString</key> 20 | <string>1.0.1</string> 21 | <key>CFBundleURLTypes</key> 22 | <array> 23 | <dict> 24 | <key>CFBundleTypeRole</key> 25 | <string>Editor</string> 26 | <key>CFBundleURLSchemes</key> 27 | <array> 28 | <string>testbed-mac</string> 29 | </array> 30 | </dict> 31 | </array> 32 | <key>CFBundleVersion</key> 33 | <string>5</string> 34 | <key>LSApplicationCategoryType</key> 35 | <string>public.app-category.developer-tools</string> 36 | <key>LSMinimumSystemVersion</key> 37 | <string>$(MACOSX_DEPLOYMENT_TARGET)</string> 38 | <key>LSMultipleInstancesProhibited</key> 39 | <true/> 40 | <key>NSAppTransportSecurity</key> 41 | <dict> 42 | <key>NSAllowsArbitraryLoads</key> 43 | <true/> 44 | </dict> 45 | <key>NSHumanReadableCopyright</key> 46 | <string>Copyright © 2018 Branch. All rights reserved.</string> 47 | <key>NSMainNibFile</key> 48 | <string>MainMenu</string> 49 | <key>NSPrincipalClass</key> 50 | <string>NSApplication</string> 51 | <key>NSRequiresAquaSystemAppearance</key> 52 | <true/> 53 | </dict> 54 | </plist> 55 | -------------------------------------------------------------------------------- /BranchTests/BNCTestNetworkService.Test.m: -------------------------------------------------------------------------------- 1 | /** 2 | @file BNCTestNetworkService.Test.m 3 | @package BranchTests 4 | @brief Test the BNCTestNetworkService. 5 | 6 | @author Edward Smith 7 | @date June 6, 2018 8 | @copyright Copyright © 2018 Branch. All rights reserved. 9 | */ 10 | 11 | #import "BNCTestNetworkService.h" 12 | #import "BNCTestCase.h" 13 | #import "Branch.h" 14 | 15 | @interface BNCTestNetworkServiceTest : BNCTestCase 16 | @end 17 | 18 | @implementation BNCTestNetworkServiceTest 19 | 20 | - (void) testTheTestService { 21 | BranchConfiguration*config = [[BranchConfiguration alloc] initWithKey:@"key_live_12345"]; 22 | config.networkServiceClass = [BNCTestNetworkService class]; 23 | Branch*branch = [[Branch alloc] init]; 24 | [branch startWithConfiguration:config]; 25 | 26 | XCTestExpectation*requestExpectation = [self expectationWithDescription:@"testTheTestService-1"]; 27 | BNCTestNetworkService.requestHandler = ^ id<BNCNetworkOperationProtocol> (NSMutableURLRequest*request) { 28 | XCTAssertEqualObjects(request.HTTPMethod, @"POST"); 29 | XCTAssertEqualObjects(request.URL.path, @"/v1/install"); 30 | NSMutableDictionary*truthDictionary = [self mutableDictionaryFromBundleJSONWithKey:@"BranchInstallRequestMac"]; 31 | NSMutableDictionary*requestDictionary = [BNCTestNetworkService mutableDictionaryFromRequest:request]; 32 | XCTAssertNotNil(truthDictionary); 33 | XCTAssertNotNil(requestDictionary); 34 | 35 | [requestExpectation fulfill]; 36 | NSString*responseString = [self stringFromBundleJSONWithKey:@"BranchOpenResponseMac"]; 37 | return [BNCTestNetworkService operationWithRequest:request response:responseString]; 38 | }; 39 | 40 | [self waitForExpectationsWithTimeout:5.0 handler:nil]; 41 | } 42 | 43 | @end 44 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | # Mac 6 | .DS_Store 7 | .LSOverride 8 | 9 | # merge-repo commit/discard file 10 | merge-repo-commit 11 | 12 | ## Build generated 13 | [Bb]uild/ 14 | DerivedData/ 15 | 16 | ## Various settings 17 | *.pbxuser 18 | !default.pbxuser 19 | *.mode1v3 20 | !default.mode1v3 21 | *.mode2v3 22 | !default.mode2v3 23 | *.perspectivev3 24 | !default.perspectivev3 25 | xcuserdata/ 26 | 27 | ## Other 28 | *.moved-aside 29 | *.xccheckout 30 | *.xcscmblueprint 31 | 32 | ## Obj-C/Swift specific 33 | *.hmap 34 | *.ipa 35 | *.dSYM.zip 36 | *.dSYM 37 | 38 | # CocoaPods 39 | # 40 | # We recommend against adding the Pods directory to your .gitignore. However 41 | # you should judge for yourself, the pros and cons are mentioned at: 42 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 43 | # 44 | Pods/ 45 | 46 | # Carthage 47 | # 48 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 49 | # Carthage/Checkouts 50 | 51 | Carthage/Build 52 | 53 | # fastlane 54 | # 55 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 56 | # screenshots whenever they are needed. 57 | # For more information about the recommended setup visit: 58 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 59 | 60 | fastlane/report.xml 61 | fastlane/Preview.html 62 | fastlane/screenshots 63 | fastlane/test_output 64 | 65 | # Code Injection 66 | # 67 | # After new code Injection tools there's a generated folder /iOSInjectionProject 68 | # https://github.com/johnno1962/injectionforxcode 69 | 70 | iOSInjectionProject/ 71 | 72 | # ignore release build product. this is attached to the github release page 73 | Branch.zip 74 | -------------------------------------------------------------------------------- /Branch/BNCNetworkAPIService.h: -------------------------------------------------------------------------------- 1 | /** 2 | @file BNCNetworkAPIService.h 3 | @package Branch 4 | @brief Branch API network service interface. 5 | 6 | @author Edward Smith 7 | @date May 2018 8 | @copyright Copyright © 2018 Branch. All rights reserved. 9 | */ 10 | 11 | #import "BranchHeader.h" 12 | #import "BranchSession.h" 13 | #import "BranchNetworkServiceProtocol.h" 14 | @class BranchConfiguration; 15 | 16 | NS_ASSUME_NONNULL_BEGIN 17 | 18 | #pragma mark BNCNetworkAPIOperation 19 | 20 | @interface BNCNetworkAPIOperation : NSOperation 21 | @property (atomic, strong) id<BNCNetworkOperationProtocol> operation; 22 | @property (atomic, strong) NSDate*_Nullable startDate; 23 | @property (atomic, strong) NSDate*_Nullable timeoutDate; 24 | @property (atomic, strong) NSError*_Nullable error; 25 | @property (atomic, strong) BranchSession*_Nullable session; 26 | @end 27 | 28 | #pragma mark - BNCNetworkAPIService 29 | 30 | @interface BNCNetworkAPIService : NSObject 31 | - (instancetype) initWithConfiguration:(BranchConfiguration*)configuration; 32 | 33 | - (void) appendV1APIParametersWithDictionary:(NSMutableDictionary*)dictionary; 34 | - (void) appendV2APIParametersWithDictionary:(NSMutableDictionary*)dictionary; 35 | 36 | /** 37 | @param serviceName The Branch end point name, like "v2/event" or "v1/open". 38 | @param dictionary The dictionary for the JSON post content. 39 | @param completion The completion block that receives the response data. 40 | */ 41 | - (void) postOperationForAPIServiceName:(NSString*)serviceName 42 | dictionary:(NSMutableDictionary*)dictionary 43 | completion:(void (^_Nullable)(BNCNetworkAPIOperation*operation))completion; 44 | 45 | - (void) clearNetworkQueue; 46 | @property (atomic, assign, getter=queueIsPaused) BOOL queuePaused; 47 | @property (atomic, assign, readonly) NSInteger queueDepth; 48 | @end 49 | 50 | NS_ASSUME_NONNULL_END 51 | -------------------------------------------------------------------------------- /BranchTests/BranchUserTrackingDisabled.m: -------------------------------------------------------------------------------- 1 | // 2 | /** 3 | @file BranchUserTrackingDisabled.m 4 | @package BranchTests 5 | @brief Tests creation of short link when tracking is disabled. 6 | 7 | @author Nidhi Dixit 8 | @date 2020 9 | @copyright Copyright © 2020 Branch. All rights reserved. 10 | */ 11 | 12 | #import <XCTest/XCTest.h> 13 | #import "BNCTestCase.h" 14 | #import "BranchError.h" 15 | #import "BNCLog.h" 16 | 17 | @interface BranchUserTrackingDisabled : XCTestCase 18 | @property (strong) Branch *branch; 19 | @end 20 | 21 | @implementation BranchUserTrackingDisabled 22 | 23 | - (void)setUp { 24 | if (!self.branch) { 25 | self.branch = [[Branch alloc] init]; 26 | [self.branch startWithConfiguration:[[BranchConfiguration alloc] initWithKey:BNCTestBranchKey]]; 27 | self.branch.userTrackingDisabled = YES; 28 | } 29 | } 30 | 31 | - (void)tearDown { 32 | if (self.branch) 33 | self.branch.userTrackingDisabled = NO; 34 | } 35 | 36 | - (void)testShortLink { 37 | 38 | BranchUniversalObject *buo = [[BranchUniversalObject alloc] initWithCanonicalIdentifier:@"id-123"]; 39 | buo.title = @"Test link"; 40 | buo.canonicalUrl = @"https://branch.io/docs/unit-tests"; 41 | BranchLinkProperties *lp = [[BranchLinkProperties alloc] init]; 42 | lp.channel = @"UnitTests"; 43 | XCTestExpectation *expectation = [self expectationWithDescription:@"testShortLinks"]; 44 | [self.branch branchShortLinkWithContent:buo linkProperties:lp completion: 45 | ^ (NSURL * _Nullable shortURL, NSError * _Nullable error) { 46 | XCTAssertNil(error); 47 | XCTAssertNotNil(shortURL); 48 | XCTAssertTrue([shortURL.absoluteString hasPrefix:@"https://testbed-mac.app.link/"]); 49 | [expectation fulfill]; 50 | }]; 51 | [self waitForExpectationsWithTimeout:5.0 handler:nil]; 52 | 53 | 54 | } 55 | 56 | 57 | @end 58 | -------------------------------------------------------------------------------- /BranchTests/BNCTestNetworkService.h: -------------------------------------------------------------------------------- 1 | /** 2 | @file BNCTestNetworkService.h 3 | @package BranchTests 4 | @brief A class for mocking network service calls. 5 | 6 | @author Edward Smith 7 | @date June 2018 8 | @copyright Copyright © 2018 Branch. All rights reserved. 9 | */ 10 | 11 | #import <Foundation/Foundation.h> 12 | #import "BranchNetworkServiceProtocol.h" 13 | #import "BNCNetworkAPIService.h" 14 | 15 | NS_ASSUME_NONNULL_BEGIN 16 | 17 | #pragma mark BNCTestNetworkOperation 18 | 19 | @interface BNCTestNetworkOperation : NSObject <BNCNetworkOperationProtocol> 20 | @property (strong) NSMutableURLRequest* request; 21 | @property (assign) NSInteger HTTPStatusCode; 22 | @property (strong) NSError*_Nullable error; 23 | //@property (strong) NSDate*_Nullable startDate; 24 | //@property (strong) NSDate*_Nullable timeoutDate; 25 | @property (strong) NSData*_Nullable responseData; 26 | //@property (strong) NSDictionary*userInfo; 27 | - (void) start; 28 | - (void) cancel; 29 | @end 30 | 31 | #pragma mark - BNCTestNetworkService 32 | 33 | @interface BNCTestNetworkService : NSObject <BNCNetworkServiceProtocol> 34 | 35 | - (id<BNCNetworkOperationProtocol>) networkOperationWithURLRequest:(NSMutableURLRequest*)request 36 | completion:(void (^)(id<BNCNetworkOperationProtocol>operation))completion; 37 | 38 | //@property (atomic, strong) NSDictionary*_Nullable userInfo; 39 | 40 | // Properties and methods for mocking tests: 41 | 42 | @property (atomic, class, copy) id<BNCNetworkOperationProtocol>(^_Nullable requestHandler)(NSMutableURLRequest*request); 43 | 44 | + (NSMutableDictionary*_Nullable) mutableDictionaryFromRequest:(NSURLRequest*)request; 45 | 46 | + (id<BNCNetworkOperationProtocol>) operationWithRequest:(NSMutableURLRequest*)request 47 | response:(NSString*_Nullable)responseString; 48 | @end 49 | 50 | NS_ASSUME_NONNULL_END 51 | 52 | -------------------------------------------------------------------------------- /BranchTests/NSData+Branch.Test.m: -------------------------------------------------------------------------------- 1 | /** 2 | @file NSData+Branch.Test.m 3 | @package BranchTests 4 | @brief Tests for the NSData+Branch category. 5 | 6 | @author Edward Smith 7 | @date June 10, 2018 8 | @copyright Copyright © 2018 Branch. All rights reserved. 9 | */ 10 | 11 | #import "BNCTestCase.h" 12 | #import "NSData+Branch.h" 13 | 14 | @interface NSDataBranchTest : BNCTestCase 15 | @end 16 | 17 | @implementation NSDataBranchTest 18 | 19 | - (void) testHexDecode { 20 | char bytes[] = { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef }; 21 | NSData*truthData = [NSData dataWithBytes:bytes length:8]; 22 | NSData*data = [NSData bnc_dataWithHexString:@"0123456789abcdef"]; 23 | XCTAssertEqualObjects(truthData, data); 24 | 25 | data = [NSData bnc_dataWithHexString:@"0123456789ABCDEF"]; 26 | XCTAssertEqualObjects(truthData, data); 27 | } 28 | 29 | - (void) testHexDecodeOddBytes { 30 | char bytes[] = { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xe0 }; 31 | NSData*truthData = [NSData dataWithBytes:bytes length:8]; 32 | NSData*data = [NSData bnc_dataWithHexString:@"0123456789abcde"]; 33 | XCTAssertEqualObjects(truthData, data); 34 | } 35 | 36 | - (void) testHexDecodeTricky { 37 | char bytes[] = { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef }; 38 | NSData*truthData = [NSData dataWithBytes:bytes length:8]; 39 | 40 | NSData*data = [NSData bnc_dataWithHexString:@""]; 41 | XCTAssertTrue(data != nil && data.length == 0); 42 | 43 | data = [NSData bnc_dataWithHexString:@" XXX 012345678 9ab cde\nf"]; 44 | XCTAssertEqualObjects(truthData, data); 45 | } 46 | 47 | - (void) testHighLowBytes { 48 | char bytes[] = { 0x00, 0x01, 0x7f, 0x80, 0xff }; 49 | NSData*truthData = [NSData dataWithBytes:bytes length:5]; 50 | NSData*data = [NSData bnc_dataWithHexString:@"0001 7f80 ff"]; 51 | XCTAssertEqualObjects(truthData, data); 52 | } 53 | 54 | @end 55 | -------------------------------------------------------------------------------- /BranchTests/BranchMutableDictionary.Test.m: -------------------------------------------------------------------------------- 1 | /** 2 | @file BranchMutableDictionary.Test.m 3 | @package BranchTests 4 | @brief BranchMutableDictionary tests. 5 | 6 | @author Edward Smith 7 | @date July 2018 8 | @copyright Copyright © 2018 Branch. All rights reserved. 9 | */ 10 | 11 | #import "BNCTestCase.h" 12 | #import "BranchMutableDictionary.h" 13 | 14 | @interface BranchMutableDictionaryTest : BNCTestCase 15 | @end 16 | 17 | @implementation BranchMutableDictionaryTest 18 | 19 | - (void)testDictionary { 20 | NSDictionary*truth = @{ 21 | @"key1": @"value1", 22 | @"key2": @"value2", 23 | @"key3": @"value3" 24 | }; 25 | 26 | BranchMutableDictionary *d = [[BranchMutableDictionary alloc] init]; 27 | d[@"key1"] = @"value1"; 28 | d[@"key2"] = @"value2"; 29 | XCTAssertEqual(d.count, 2); 30 | [d setObject:@"value3" forKey:@"key3"]; 31 | XCTAssertEqualObjects(d[@"key2"], @"value2"); 32 | XCTAssertEqualObjects(d, truth); 33 | 34 | BranchMutableDictionary *c = [BranchMutableDictionary dictionaryWithDictionary:truth]; 35 | XCTAssertTrue([c isKindOfClass:BranchMutableDictionary.class]); 36 | XCTAssertEqualObjects(c, truth); 37 | 38 | BranchMutableDictionary *e = [c copy]; 39 | XCTAssertTrue([e isKindOfClass:BranchMutableDictionary.class]); 40 | XCTAssertEqualObjects(e, truth); 41 | } 42 | 43 | - (void) testCoding { 44 | NSDictionary*truth = @{ 45 | @"key1": @"value1", 46 | @"key2": @"value2", 47 | @"key3": @"value3" 48 | }; 49 | BranchMutableDictionary*d = [BranchMutableDictionary dictionaryWithDictionary:truth]; 50 | NSData*data = [NSKeyedArchiver archivedDataWithRootObject:d]; 51 | BranchMutableDictionary*e = [NSKeyedUnarchiver unarchiveObjectWithData:data]; 52 | XCTAssertTrue([e isKindOfClass:BranchMutableDictionary.class]); 53 | XCTAssertEqualObjects(e, truth); 54 | } 55 | 56 | @end 57 | -------------------------------------------------------------------------------- /Branch/BNCSettings.h: -------------------------------------------------------------------------------- 1 | /** 2 | @file BNCSettings.h 3 | @package Branch 4 | @brief Branch SDK persistent settings. 5 | 6 | @author Edward Smith 7 | @date May 2018 8 | @copyright Copyright © 2018 Branch. All rights reserved. 9 | */ 10 | 11 | #import "BranchHeader.h" 12 | #import "BranchMutableDictionary.h" 13 | 14 | NS_ASSUME_NONNULL_BEGIN 15 | 16 | @interface BNCSettings : NSObject <NSSecureCoding> 17 | + (instancetype) loadSettings; 18 | - (instancetype) init NS_DESIGNATED_INITIALIZER; 19 | - (void) clearAllSettings; 20 | - (void) clearUserIdentifyingInformation; 21 | - (void) setNeedsSave; 22 | - (void) save; 23 | @property (atomic, copy) void (^_Nullable settingsSavedBlock)(BNCSettings*settings, NSError*_Nullable error); 24 | @property (atomic, copy) NSString*_Nullable randomizedDeviceToken; 25 | @property (atomic, copy) NSString*_Nullable randomizedBundleToken; 26 | @property (atomic, copy) NSString*_Nullable deviceFingerprintID; 27 | @property (atomic, copy) NSString*_Nullable identityID; 28 | @property (atomic, copy) NSString*_Nullable userIdentityForDeveloper; 29 | @property (atomic, copy) NSString*_Nullable sessionID; 30 | @property (atomic, copy) NSString*_Nullable linkCreationURL; 31 | @property (atomic, assign) BOOL limitFacebookTracking; 32 | @property (atomic, assign) BOOL userTrackingDisabled; 33 | 34 | // URL Black list settings: 35 | 36 | @property (atomic, assign) NSInteger URLBlackListVersion; 37 | @property (atomic, copy) NSDate*_Nullable URLBlackListLastRefreshDate; 38 | @property (atomic, copy) NSArray<NSString*>*_Nullable URLBlackList; 39 | 40 | @property (atomic, strong, null_resettable) 41 | BranchMutableDictionary<NSString*, NSString*> *requestMetadataDictionary; 42 | @property (atomic, strong, null_resettable) 43 | BranchMutableDictionary<NSString*, NSString*> *instrumentationDictionary; 44 | 45 | @end 46 | 47 | NS_ASSUME_NONNULL_END 48 | -------------------------------------------------------------------------------- /Examples/TestBed-macOS/TestBed-macOSUITests/TestBedUISetIdentityTest.m: -------------------------------------------------------------------------------- 1 | // 2 | // TestBedUISetIdentityTest.m 3 | // TestBed-macOSUITests 4 | // 5 | // Created by Nidhi on 11/6/20. 6 | // Copyright © 2020 Branch. All rights reserved. 7 | // 8 | 9 | 10 | #import "TestBedUITest.h" 11 | #import "TestBedUIUtils.h" 12 | 13 | @interface TestBedUISetIdentityTest : TestBedUITest 14 | 15 | @end 16 | 17 | @implementation TestBedUISetIdentityTest 18 | 19 | - (void)setUp { 20 | [super setUp]; 21 | } 22 | 23 | - (void)tearDown { 24 | [super tearDown]; 25 | } 26 | 27 | - (void)testSetIdentityNLogout { 28 | 29 | XCTWaiterResult result = [self launchAppAndWaitForSessionStart]; 30 | 31 | if (result == XCTWaiterResultCompleted) { 32 | 33 | if (self.trackingState == TRACKING_DISABLED) { 34 | [self enableTracking]; 35 | } 36 | 37 | [self logOut]; 38 | 39 | [self setIdentity]; 40 | 41 | XCTAssertTrue([[self serverRequestString] containsString:@"/v1/profile"]); 42 | 43 | NSDictionary *serverRequest = [TestBedUIUtils dictionaryFromString:[self serverRequestString]]; 44 | XCTAssertNotNil([serverRequest valueForKey:@"randomized_bundle_token"]); 45 | XCTAssertNotNil([serverRequest valueForKey:@"identity"]); 46 | 47 | XCTAssertTrue([[self getErrorString] isEqualToString:@"< None >"]); 48 | 49 | // TODO : Check for subsequent calls to log events should include the identity specific, sent up as user_data.developer_identity 50 | 51 | // Logout 52 | [self logOut]; 53 | XCTAssertTrue([[self serverRequestString] containsString:@"/v1/logout"]); 54 | XCTAssertTrue([[self getErrorString] isEqualToString:@"< None >"]); 55 | 56 | // TODO : no subsequent requests should include the developer identity value (“a_user_name”) 57 | } 58 | else { 59 | XCTFail("App Launch / Session Start Failed."); 60 | } 61 | } 62 | 63 | @end 64 | -------------------------------------------------------------------------------- /Branch/NSData+Branch.m: -------------------------------------------------------------------------------- 1 | /** 2 | @file NSData+Branch.m 3 | @package Branch 4 | @brief NSData additions. 5 | 6 | @author Edward Smith 7 | @date June 2018 8 | @copyright Copyright © 2018 Branch. All rights reserved. 9 | */ 10 | 11 | #import "NSData+Branch.h" 12 | 13 | __attribute__((constructor)) void BNCForceNSDataCategoryToLoad() { 14 | // Nothing here, but forces linker to load the category. 15 | } 16 | 17 | @implementation NSData (Branch) 18 | 19 | static inline int16_t nibble(UniChar c) { 20 | if (c >= '0' && c <= '9') 21 | return c - '0'; 22 | else 23 | if (c >= 'a' && c <= 'f') 24 | return c - 'a' + 10; 25 | else 26 | if (c >= 'A' && c <= 'F') 27 | return c - 'A' + 10; 28 | else 29 | return -1; 30 | } 31 | 32 | + (NSData*) bnc_dataWithHexString:(NSString*)string { 33 | uint8_t*bytes = NULL; 34 | NSData*data = nil; 35 | { 36 | NSUInteger stringLength = string.length; 37 | CFStringInlineBuffer stringBuffer; 38 | CFStringInitInlineBuffer((CFStringRef)string, &stringBuffer, CFRangeMake(0, stringLength)); 39 | 40 | UniChar c; 41 | int16_t n, lastNibble = -1; 42 | NSUInteger idx = 0; 43 | uint8_t*p = bytes = malloc(stringLength/2+1); 44 | while (idx < stringLength) { 45 | c = CFStringGetCharacterFromInlineBuffer(&stringBuffer, idx++); 46 | n = nibble(c); 47 | if (n < 0) continue; 48 | if (lastNibble < 0) 49 | lastNibble = n; 50 | else { 51 | *p++ = (uint8_t) (uint16_t) (lastNibble << 4 | n); 52 | lastNibble = -1; 53 | } 54 | } 55 | if (lastNibble >= 0) 56 | *p++ = (uint8_t) (uint16_t) (lastNibble << 4 | 0); 57 | data = [NSData dataWithBytesNoCopy:bytes length:p-bytes freeWhenDone:YES]; 58 | bytes = NULL; 59 | } 60 | exit: 61 | if (bytes) free(bytes); 62 | return data; 63 | } 64 | 65 | @end 66 | -------------------------------------------------------------------------------- /Branch/BranchError.h: -------------------------------------------------------------------------------- 1 | /** 2 | @file BranchError.h 3 | @package Branch 4 | @brief Branch errors. 5 | 6 | @author Qinwei Gong 7 | @date November 2014 8 | @copyright Copyright © 2014 Branch. All rights reserved. 9 | */ 10 | 11 | #import "BranchHeader.h" 12 | 13 | NS_ASSUME_NONNULL_BEGIN 14 | 15 | 16 | #ifndef BranchError_h 17 | #define BranchError_h 18 | 19 | /// The Branch error domain. 20 | FOUNDATION_EXPORT NSString*const BNCErrorDomain; 21 | 22 | /// Branch specific error codes. 23 | typedef NS_ENUM(NSInteger, BNCErrorCode) { 24 | BNCInitError = 1000, //!< The main Branch instance wasn't initialized correctly. 25 | BNCDuplicateResourceError = 1001, //!< A requested resource, such as a short link, already exists. 26 | BNCRedeemCreditsError = 1002, //!< The credits could not be redeemed. 27 | BNCBadRequestError = 1003, 28 | BNCServerProblemError = 1004, 29 | BNCNilLogError = 1005, //!< Not used at the moment. 30 | BNCVersionError = 1006, //!< Not used at the moment. 31 | BNCNetworkServiceInterfaceError = 1007, 32 | BNCContentIdentifierError = 1008, 33 | BNCSpotlightNotAvailableError = 1009, 34 | BNCSpotlightTitleError = 1010, 35 | BNCRedeemZeroCreditsError = 1011, 36 | BNCSpotlightIdentifierError = 1012, 37 | BNCSpotlightPublicIndexError = 1013, 38 | BNCTrackingDisabledError = 1014, 39 | BNCHighestError, 40 | }; 41 | 42 | /** 43 | A convenience category for creating Branch errors. 44 | */ 45 | @interface NSError (Branch) 46 | + (NSError*) branchErrorWithCode:(BNCErrorCode)errorCode; 47 | + (NSError*) branchErrorWithCode:(BNCErrorCode)errorCode error:(NSError*_Nullable)error; 48 | + (NSError*) branchErrorWithCode:(BNCErrorCode)errorCode localizedMessage:(NSString*_Nullable)message; 49 | @end 50 | 51 | /// 52 | FOUNDATION_EXPORT void BNCForceNSErrorCategoryToLoad(void) 53 | __attribute__((constructor)); 54 | 55 | #endif 56 | 57 | NS_ASSUME_NONNULL_END 58 | -------------------------------------------------------------------------------- /Branch/BNCDevice.h: -------------------------------------------------------------------------------- 1 | /** 2 | @file BNCDevice.h 3 | @package Branch 4 | @brief Device information. 5 | 6 | @author Edward Smith 7 | @date May 2018 8 | @copyright Copyright © 2018 Branch. All rights reserved. 9 | */ 10 | 11 | #import "BranchHeader.h" 12 | 13 | NS_ASSUME_NONNULL_BEGIN 14 | 15 | @interface BNCDevice : NSObject 16 | 17 | + (instancetype) currentDevice; 18 | - (NSMutableDictionary*) v1dictionary; 19 | - (NSMutableDictionary*) v2dictionary; 20 | 21 | @property (atomic, copy, readonly) NSString *hardwareID; 22 | @property (atomic, copy, readonly) NSString *hardwareIDType; //!< vendor_id, idfa, or random 23 | @property (atomic, assign, readonly) BOOL deviceIsUnidentified; 24 | @property (atomic, copy, readonly) NSString *brandName; 25 | @property (atomic, copy, readonly) NSString *modelName; 26 | @property (atomic, copy, readonly) NSString *systemName; 27 | @property (atomic, copy, readonly) NSString *systemVersion; 28 | @property (atomic, copy, readonly) NSString *systemBuildVersion; 29 | @property (atomic, assign, readonly) BOOL isSimulator; 30 | @property (atomic, assign, readonly) CGSize screenSize; 31 | @property (atomic, assign, readonly) CGFloat screenDPI; 32 | @property (atomic, assign, readonly) BOOL adTrackingIsEnabled; //!< True if advertisingID is available. 33 | @property (atomic, copy, readonly) NSString*_Nullable advertisingID; //!< iOS identifierForAdvertisor. 34 | @property (atomic, copy, readonly) NSString*_Nullable vendorID; //!< iOS identifierForVendor. 35 | @property (atomic, copy, readonly) NSString*_Nullable netAddress; //!< Network card address. 36 | @property (atomic, copy, readonly) NSString *country; //!< The iso2 Country name (us, in,etc). 37 | @property (atomic, copy, readonly) NSString *language; //!< The iso2 language code (en, ml). 38 | @property (atomic, copy, readonly) NSString *localIPAddress; //!< The current local IPv4 address. 39 | @property (atomic, copy, readonly) NSArray<NSString*> *allLocalIPAddresses; //!< All local IP addresses. 40 | 41 | @property (atomic, copy, readonly) NSString *userAgent; 42 | 43 | @end 44 | 45 | NS_ASSUME_NONNULL_END 46 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug-report.yml: -------------------------------------------------------------------------------- 1 | --- 2 | assignees: [] 3 | body: 4 | - 5 | attributes: 6 | description: "What is the problem? A clear and concise description of what the bug is." 7 | label: "Describe the bug" 8 | placeholder: "Tell us what you see!" 9 | id: description 10 | type: textarea 11 | validations: 12 | required: true 13 | - 14 | attributes: 15 | description: "Please provide as much step-by-step detail as possible including logs, stack traces, and uncaught exceptions." 16 | label: "Steps to reproduce" 17 | value: | 18 | 1. 19 | 2. 20 | 3. 21 | id: steps 22 | type: textarea 23 | validations: 24 | required: true 25 | - 26 | attributes: 27 | description: "What did you expect to happen?" 28 | label: "Expected behavior" 29 | id: expected 30 | type: textarea 31 | validations: 32 | required: true 33 | - 34 | attributes: 35 | description: "What version of sdk are you seeing this issue on?" 36 | label: "SDK Version" 37 | placeholder: "1.4.0" 38 | id: sdk-version 39 | type: input 40 | validations: 41 | required: true 42 | - 43 | attributes: 44 | description: "What version of Xcode are you using?" 45 | label: "XCode Version" 46 | placeholder: "13.4.1" 47 | id: xcode-version 48 | type: input 49 | validations: 50 | required: true 51 | - 52 | attributes: 53 | description: "What is the version of macOS?" 54 | label: OS 55 | placeholder: "12.5.1" 56 | id: os 57 | type: input 58 | validations: 59 | required: true 60 | - 61 | attributes: 62 | description: "Anything else that might be relevant for troubleshooting this bug. Any screenshots or videos that show the issue are very helpful." 63 | label: "Additional Information/Context" 64 | id: context 65 | type: textarea 66 | validations: 67 | required: false 68 | 69 | description: "Found a bug in the Branch macOS SDK? File it here." 70 | labels: 71 | - bug 72 | - needs-triage 73 | name: "🐞 Bug report" 74 | title: "(short issue description)" 75 | -------------------------------------------------------------------------------- /Branch/BNCThreads.h: -------------------------------------------------------------------------------- 1 | /** 2 | @file BNCThreads.h 3 | @package Branch 4 | @brief Utilities for working with threads, queues, and blocks. 5 | 6 | @author Edward Smith 7 | @date May 2018 8 | @copyright Copyright © 2018 Branch. All rights reserved. 9 | */ 10 | 11 | #import "BranchHeader.h" 12 | 13 | NS_ASSUME_NONNULL_BEGIN 14 | 15 | ///@name Blocks and Threads 16 | #pragma mark - Blocks and Threads 17 | 18 | static inline uint64_t BNCNanoSecondsFromTimeInterval(NSTimeInterval interval) { 19 | return interval * ((NSTimeInterval) NSEC_PER_SEC); 20 | } 21 | 22 | static inline dispatch_time_t BNCDispatchTimeFromSeconds(NSTimeInterval seconds) { 23 | return dispatch_time(DISPATCH_TIME_NOW, seconds * NSEC_PER_SEC); 24 | } 25 | 26 | static inline void BNCAfterSecondsPerformBlockOnMainThread(NSTimeInterval seconds, dispatch_block_t block) { 27 | dispatch_after(BNCDispatchTimeFromSeconds(seconds), dispatch_get_main_queue(), block); 28 | } 29 | 30 | static inline void BNCAfterSecondsPerformBlock(NSTimeInterval seconds, dispatch_block_t block) { 31 | dispatch_queue_t queue = dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0); 32 | dispatch_after(BNCDispatchTimeFromSeconds(seconds), queue, block); 33 | } 34 | 35 | static inline void BNCPerformBlockOnMainThreadAsync(dispatch_block_t block) { 36 | dispatch_async(dispatch_get_main_queue(), block); 37 | } 38 | 39 | static inline void BNCPerformBlockAsync(dispatch_block_t block) { 40 | dispatch_queue_t queue = dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0); 41 | dispatch_async(queue, block); 42 | } 43 | 44 | static inline void BNCPerformBlockOnMainThreadSync(dispatch_block_t block) { 45 | if ([NSThread isMainThread]) 46 | block(); 47 | else 48 | dispatch_sync(dispatch_get_main_queue(), block); 49 | } 50 | 51 | static inline void BNCSleepForTimeInterval(NSTimeInterval seconds) { 52 | double secPart = trunc(seconds); 53 | double nanoPart = trunc((seconds - secPart) * ((double)NSEC_PER_SEC)); 54 | struct timespec sleepTime; 55 | sleepTime.tv_sec = (__typeof(sleepTime.tv_sec)) secPart; 56 | sleepTime.tv_nsec = (__typeof(sleepTime.tv_nsec)) nanoPart; 57 | nanosleep(&sleepTime, NULL); 58 | } 59 | 60 | NS_ASSUME_NONNULL_END 61 | -------------------------------------------------------------------------------- /BranchTests/BNCDevice.Test.m: -------------------------------------------------------------------------------- 1 | /** 2 | @file BNCDevice.Test.m 3 | @package BranchTests 4 | @brief Tests for BNCDevice. 5 | 6 | @author Edward Smith 7 | @date May 2018 8 | @copyright Copyright © 2018 Branch. All rights reserved. 9 | */ 10 | 11 | #import "BNCTestCase.h" 12 | #import "BNCDevice.h" 13 | 14 | @interface BNCDeviceTest : BNCTestCase 15 | @end 16 | 17 | @implementation BNCDeviceTest 18 | 19 | - (void)testDevice { 20 | BNCDevice *device = [BNCDevice currentDevice]; 21 | XCTAssertTrue(device.hardwareID.length > 0); 22 | XCTAssertTrue( 23 | [device.hardwareIDType isEqualToString:@"idfa"] || 24 | [device.hardwareIDType isEqualToString:@"random"] || 25 | [device.hardwareIDType isEqualToString:@"mac_address"] 26 | ); 27 | XCTAssertFalse(device.deviceIsUnidentified); 28 | XCTAssertTrue([device.brandName isEqualToString:@"Apple"]); 29 | 30 | XCTAssertTrue([device.modelName hasPrefix:@"Mac"]); 31 | XCTAssertTrue([device.systemName isEqualToString:@"mac_OS"]); 32 | XCTAssertTrue(device.hardwareID.length > 0); 33 | 34 | XCTAssertTrue( 35 | device.systemVersion.doubleValue > 10.15 && 36 | device.systemVersion.doubleValue <= 14 37 | ); 38 | XCTAssertTrue(BNCTestStringMatchesRegex(device.systemBuildVersion, @"^[0-9A-Za-z]+$")); 39 | XCTAssertTrue( 40 | device.screenSize.height > 0 && 41 | device.screenSize.width > 0 42 | ); 43 | 44 | XCTAssertTrue(device.screenDPI >= 72.0 && device.screenDPI <= 216.0); 45 | 46 | // idfa is broken on 10.15+ 47 | if ([self testDeviceSupportsIDFA]) { 48 | XCTAssertTrue(device.adTrackingIsEnabled); 49 | XCTAssertNotNil(device.advertisingID); 50 | } else { 51 | XCTAssertFalse(device.adTrackingIsEnabled); 52 | XCTAssertNil(device.advertisingID); 53 | } 54 | XCTAssertTrue([device.country isEqualToString:@"US"]); 55 | XCTAssertTrue([device.language isEqualToString:@"en"]); 56 | XCTAssertTrue(BNCTestStringMatchesRegex(device.localIPAddress, @"^\\d*\\.\\d*\\.\\d*\\.\\d*$")); 57 | } 58 | 59 | - (void)testIPAddresses { 60 | NSArray *d = [[BNCDevice currentDevice] allLocalIPAddresses]; 61 | XCTAssertGreaterThan(d.count, 1); 62 | NSLog(@"IP Addresses: %@.", d); 63 | } 64 | 65 | @end 66 | -------------------------------------------------------------------------------- /Scripts/deploy-release: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euo pipefail 3 | 4 | # deploy-release - The release deployment master script. 5 | # 6 | # This script is written to be excessively modular so it can be debugged or restarted easily. 7 | # 8 | # Edward Smith, December 2016 9 | 10 | scriptfile="$( cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 11 | scriptfile="${scriptfile}"/$(basename "$0") 12 | cd $(dirname "$scriptfile")/.. 13 | 14 | scriptFailed=1 15 | function finish { 16 | if [ $scriptFailed -ne 0 ]; then 17 | echo ">>> Error: `basename "$scriptfile"` failed!" 1>&2 18 | exit 1 19 | fi 20 | } 21 | trap finish EXIT 22 | 23 | 24 | version=$(./scripts/version) 25 | 26 | echo "" 27 | echo "Before continuing:" 28 | echo "- Make sure that the release version number is already updated." 29 | echo "- Make sure that the ChangeLog.md has been updated, spell checked, and is coherent." 30 | echo "- Make sure that you are logged in to your Branch Google account and have write access to Google Groups." 31 | echo "" 32 | if ! ./scripts/askYN "Deploy Branch release version ${version}?"; then 33 | echo ">>> Nothing deployed." 1>&2 34 | exit 1 35 | fi 36 | 37 | # Check that deployment software is installed 38 | ./scripts/deploy-preflight 39 | 40 | # Prompt for editor input for ChangeLog. 41 | #vim +4 +star ChangeLog.md 42 | nano ChangeLog.md 43 | 44 | # Pre-release CocoaPod lint 45 | echo ">>> Linting build for release..." 1>&2 46 | 47 | # Validate the podspec 48 | pod lib lint BranchMacOS.podspec --verbose 49 | 50 | # Build the framework 51 | echo ">>> Building the framework..." 1>&2 52 | ./scripts/deploy-build-framework 53 | 54 | # Make the zip files and get the checksums 55 | echo ">>> Zipping and checksumming framework files..." 1>&2 56 | ./scripts/deploy-checksum 57 | 58 | echo '>>> The Branch SDK build is successful so far.' 1>&2 59 | if ! ./scripts/askYN "Commit and deploy Branch release version ${version}?"; then 60 | echo ">>> Nothing deployed." 1>&2 61 | exit 1 62 | fi 63 | 64 | # currently we have to manually commit and tag 65 | #echo '>>> Commit and tag...' 1>&2 66 | #./scripts/deploy-git-tag 67 | 68 | echo '>>> Pushing Branch CocoaPod...' 1>&2 69 | pod trunk push Branch.podspec 70 | 71 | # Prompts for SDK Release announcements 72 | ./scripts/deploy-announce 73 | 74 | echo "" 75 | echo "The Branch SDK has been released. Rejoice and pay tribute to Steve Jobs!" 76 | # Completed OK: 77 | scriptFailed=0 78 | -------------------------------------------------------------------------------- /Branch/BranchQRCode.h: -------------------------------------------------------------------------------- 1 | // 2 | // BranchQRCode.h 3 | // BranchMacOS 4 | // 5 | // Created by Nipun Singh on 5/23/22. 6 | // Copyright © 2022 Branch, Inc. All rights reserved. 7 | // 8 | 9 | #import "BranchUniversalObject.h" 10 | #import "BranchLinkProperties.h" 11 | #import "BranchHeader.h" 12 | 13 | #ifndef BranchQRCode_h 14 | #define BranchQRCode_h 15 | 16 | typedef NS_ENUM(NSInteger, BranchQRCodeImageFormat){ 17 | BranchQRCodeImageFormatPNG, 18 | BranchQRCodeImageFormatJPEG 19 | }; 20 | 21 | @interface BranchQRCode : NSObject 22 | 23 | /// Primary color of the generated QR code itself. 24 | @property (nonatomic, copy, readwrite) NSColor * _Nullable codeColor; 25 | /// Secondary color used as the QR Code background. 26 | @property (nonatomic, copy, readwrite) NSColor * _Nullable backgroundColor; 27 | /// A URL of an image that will be added to the center of the QR code. Must be a PNG or JPEG. 28 | @property (nonatomic, copy, readwrite) NSString * _Nullable centerLogo; 29 | /// Output size of QR Code image. Min 500px. Max 2000px. 30 | @property (nonatomic, readwrite) NSNumber * _Nullable width; 31 | /// The number of pixels for the QR code's border. Min 0px. Max 20px. 32 | @property (nonatomic, readwrite) NSNumber * _Nullable margin; 33 | /// Format of the returned QR code. Can be a JPEG or PNG. 34 | @property (nonatomic, assign, readwrite) BranchQRCodeImageFormat imageFormat; 35 | 36 | /** 37 | Creates a Branch QR Code image. Returns the QR code as a CIImage. 38 | 39 | @param buo The Branch Universal Object the will be shared. 40 | @param lp The link properties that the link will have. 41 | @param completion Completion handler containing the QR code image and error. 42 | 43 | */ 44 | - (void) getQRCodeAsImage:(BranchUniversalObject*_Nullable)buo 45 | linkProperties:(BranchLinkProperties*_Nullable)lp 46 | completion:(void(^_Nonnull)(CIImage * _Nullable qrCode, NSError * _Nullable error))completion; 47 | 48 | /** 49 | Creates a Branch QR Code image. Returns the QR code as NSData. 50 | 51 | @param buo The Branch Universal Object the will be shared. 52 | @param lp The link properties that the link will have. 53 | @param completion Completion handler containing the QR code image and error. 54 | 55 | */ 56 | - (void) getQRCodeAsData:(BranchUniversalObject*_Nullable)buo 57 | linkProperties:(BranchLinkProperties*_Nullable)lp 58 | completion:(void(^_Nonnull)(NSData * _Nullable qrCode, NSError * _Nullable error))completion; 59 | 60 | 61 | @end 62 | 63 | 64 | #endif /* BranchQRCode_h */ 65 | -------------------------------------------------------------------------------- /BranchTests/BranchLinkProperties.Test.m: -------------------------------------------------------------------------------- 1 | /** 2 | @file BranchLinkProperties.Test.m 3 | @package BranchTests 4 | @brief BranchLinkProperties tests. 5 | 6 | @author Edward Smith 7 | @date May 2018 8 | @copyright Copyright © 2018 Branch. All rights reserved. 9 | */ 10 | 11 | #import "BNCTestCase.h" 12 | #import "BranchLinkProperties.h" 13 | #import "BNCWireFormat.h" 14 | 15 | @interface BranchLinkPropertiesTest : BNCTestCase 16 | @end 17 | 18 | @implementation BranchLinkPropertiesTest 19 | 20 | - (void) testSerializeDeserialize { 21 | NSDictionary* open = [self mutableDictionaryFromBundleJSONWithKey:@"BranchOpenResponse"]; 22 | XCTAssertNotNil(open); 23 | NSDictionary*d1 = BNCDictionaryFromWireFormat(open[@"data"]); 24 | BranchLinkProperties *lp = [BranchLinkProperties linkPropertiesWithDictionary:d1]; 25 | NSArray*tags = @[ @"tag1", @"tag2" ]; 26 | XCTAssertEqualObjects(lp.tags, tags); 27 | XCTAssertEqualObjects(lp.feature, @"Sharing Feature"); 28 | XCTAssertEqualObjects(lp.alias, nil); 29 | XCTAssertEqualObjects(lp.channel, @"Distribution Channel"); 30 | XCTAssertEqualObjects(lp.stage, @"stage four"); 31 | XCTAssertEqualObjects(lp.campaign, @"some campaign"); 32 | XCTAssertEqual(lp.matchDuration, 0); 33 | NSDictionary*cp = @{ 34 | @"$canonical_identifier": @"item/12345", 35 | @"$canonical_url": @"https://dev.branch.io/getting-started/deep-link-routing/guide/ios/", 36 | @"$content_schema": @"some type", 37 | @"$creation_timestamp": @1527379945531, 38 | @"$currency": @"$", 39 | @"$desktop_url": @"http://branch.io", 40 | @"$randomized_bundle_token": @529056271991951584, 41 | @"$ios_url":@"https://dev.branch.io/getting-started/sdk-integration-guide/guide/ios/", 42 | @"$match_duration": @12, 43 | @"$og_description": @"My Content Description", 44 | @"$og_image_url": @"http://a57.foxnews.com/images.foxnews.com/content/fox-news/science/2018/03/20/first-day-spring-arrives-5-things-to-know-about-vernal-equinox/_jcr_content/par/featured_image/media-0.img.jpg/1862/1048/1521552912093.jpg?ve=1&tl=1", 45 | @"$og_title": @"Content Title", 46 | @"$og_type": @"website", 47 | @"$one_time_use": @0, 48 | @"$price": @1000, 49 | }; 50 | XCTAssertEqualObjects(lp.controlParams, cp); 51 | 52 | NSDictionary*d2 = [lp dictionary]; 53 | for (NSString*key in d2.keyEnumerator) { 54 | id v1 = d1[key]; 55 | id v2 = d2[key]; 56 | XCTAssertEqualObjects(v1, v2); 57 | } 58 | } 59 | 60 | @end 61 | -------------------------------------------------------------------------------- /BranchTests/BranchError.Test.m: -------------------------------------------------------------------------------- 1 | /** 2 | @file BranchError.Test.m 3 | @package BranchTests 4 | @brief Branch error tests. 5 | 6 | @author Edward Smith 7 | @date August 2017 8 | @copyright Copyright © 2017 Branch. All rights reserved. 9 | */ 10 | 11 | #import "BNCTestCase.h" 12 | #import "BranchError.h" 13 | #import "BNCLocalization.h" 14 | #import "BNCDevice.h" 15 | 16 | @interface BranchErrorTest : BNCTestCase 17 | @end 18 | 19 | @implementation BranchErrorTest 20 | 21 | - (void) testErrorBasic { 22 | 23 | NSError *error = nil; 24 | error = [NSError branchErrorWithCode:BNCInitError]; 25 | XCTAssert(error.domain == BNCErrorDomain); 26 | XCTAssert(error.code == BNCInitError); 27 | XCTAssert([error.localizedDescription isEqualToString: 28 | @"The Branch user session has not been initialized."] 29 | ); 30 | 31 | NSError *underlyingError = 32 | [NSError errorWithDomain:NSCocoaErrorDomain 33 | code:NSFileNoSuchFileError userInfo:nil]; 34 | error = [NSError branchErrorWithCode:BNCServerProblemError error:underlyingError]; 35 | XCTAssert(error.domain == BNCErrorDomain); 36 | XCTAssert(error.code == BNCServerProblemError); 37 | XCTAssert( 38 | [error.localizedDescription isEqualToString: 39 | @"Trouble reaching the Branch servers, please try again shortly."] 40 | ); 41 | XCTAssert(error.userInfo[NSUnderlyingErrorKey] == underlyingError); 42 | if ([BNCDevice currentDevice].systemVersion.floatValue < 9.0) { 43 | XCTAssert([error.localizedFailureReason isEqualToString: 44 | @"The operation couldn’t be completed. (Cocoa error 4.)"]); 45 | } else { 46 | XCTAssert([error.localizedFailureReason isEqualToString:@"The file doesn’t exist."]); 47 | } 48 | 49 | NSString *message = 50 | BNCLocalizedFormattedString( 51 | @"Network operation of class '%@' does not conform to the BNCNetworkOperationProtocol.", 52 | NSStringFromClass([self class])); 53 | error = [NSError branchErrorWithCode:BNCNetworkServiceInterfaceError localizedMessage:message]; 54 | XCTAssert(error.domain == BNCErrorDomain); 55 | XCTAssert(error.code == BNCNetworkServiceInterfaceError); 56 | XCTAssert([error.localizedDescription isEqualToString: 57 | @"The underlying network service does not conform to the BNCNetworkOperationProtocol."]); 58 | XCTAssert([error.localizedFailureReason isEqualToString: 59 | @"Network operation of class 'BranchErrorTest' does not conform to the BNCNetworkOperationProtocol."]); 60 | } 61 | 62 | @end 63 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | Branch Mac SDK Change Log 2 | 3 | ## 1.5.0 - April 30, 2024 4 | - Removed v1/profile and v1/logout requests. 5 | - Create new versions of logout() and setUserIdentity() without callbacks. 6 | 7 | ## 1.4.1 - May 9, 2023 8 | 9 | SDK-1905 10 | Fix for build issue with Xcode 14.3 and Swift 5.8 11 | 12 | INTENG-17551 13 | Added developer_identity to v2/event requests 14 | 15 | ## 1.4.0 - Aug 16, 2022 16 | 17 | SDK-1405 18 | Branch QR codes functionality added. 19 | 20 | SDK-1205 21 | Fixed Set Identity Issue. 22 | 23 | SDK-1373, SDK-1374 24 | Renamed device_fingerprint_id and identity_id to better reflect functionality. Fingerprinting was removed long ago. 25 | 26 | SDK-1346 27 | Exposed setRequestMetadata API. 28 | 29 | SDK-1530 30 | Developer id added in v1/open requests. 31 | 32 | ## v1.3.1 - Jun 2, 2021 33 | 34 | CORE-1989 35 | Add getUserIdentity to the public API 36 | 37 | CORE-1659 38 | Add INITIATE_STREAM and COMPLETE_STREAM to standard events 39 | 40 | Test coverage improvements 41 | 42 | ## v1.3.0 - Jan 28, 2021 43 | 44 | CORE-1303 45 | Improve integration options for the macOS SDK by adding support for Swift Package Manager, Carthage and Cocoapods. See the Branch docs site for more details. 46 | 47 | Improve test coverage and test automation. This may impact you if you are importing the Branch macOS SDK as source. 48 | 49 | ## v1.2.5 - Oct 8, 2020 50 | * Allow short link generation when tracking is disabled 51 | 52 | ## v1.2.4 - June 17, 2020 53 | * Remove certificate pinning 54 | 55 | ## v1.2.3 - May 13, 2020 56 | * Fix control param location in request payload 57 | 58 | ## v1.2.2 - May 7, 2020 59 | * Fix fallback when idfa is not available 60 | 61 | ## v1.2.1 - December 13, 2019 62 | * Fix crash when idfa is not available 63 | 64 | ## v1.2.0 - October 7, 2019 65 | * Add user agent 66 | * Attribution fixes 67 | 68 | ## v1.1.0 - December 6th, 2018 69 | * Added tvOS support 70 | * Added CocoaPod support 71 | 72 | ## v0.1.0-beta - *First Release - July 13, 2018* 73 | 74 | Branch is proud to release the beta version of the Branch SDK for Mac! 75 | 76 | Thank you @Sarkar, @ahmednawar, @clayjones94, @aaaronlopez and @GeneShay! 77 | 78 | We welcome your feedback, suggestions and bugs reports. You can add them in [here, in issues area here on GitHub.](https://github.com/BranchMetrics/mac-branch-deep-linking/issues) 79 | 80 | For installation and usage instructions check out the [Readme](https://github.com/BranchMetrics/mac-branch-deep-linking/blob/master/README.md) and the [documentation](https://branchmetrics.github.io/mac-branch-deep-linking/index.html). 81 | 82 | Happy linking! 83 | @E-B-Smith 84 | -------------------------------------------------------------------------------- /Branch/BranchMutableDictionary.m: -------------------------------------------------------------------------------- 1 | /** 2 | @file BranchMutableDictionary.m 3 | @package Branch 4 | @brief A thread-safe mutable dictionary. 5 | 6 | @author Edward Smith 7 | @date July 2018 8 | @copyright Copyright © 2018 Branch. All rights reserved. 9 | */ 10 | 11 | #import "BranchMutableDictionary.h" 12 | 13 | @interface BranchMutableDictionary () { 14 | NSMutableDictionary *_dictionary; 15 | } 16 | @end 17 | 18 | @implementation BranchMutableDictionary 19 | 20 | - (instancetype) init { 21 | self = [super init]; 22 | if (!self) return self; 23 | _dictionary = [[NSMutableDictionary alloc] init]; 24 | return self; 25 | } 26 | 27 | - (instancetype) initWithCapacity:(NSUInteger)numItems { 28 | self = [super init]; 29 | if (!self) return self; 30 | _dictionary = [[NSMutableDictionary alloc] initWithCapacity:numItems]; 31 | return self; 32 | } 33 | 34 | - (instancetype) initWithCoder:(NSCoder *)aDecoder { 35 | self = [super init]; 36 | if (!self) return self; 37 | _dictionary = [[NSMutableDictionary alloc] initWithCoder:aDecoder]; 38 | return self; 39 | } 40 | 41 | - (instancetype) copyWithZone:(NSZone *)zone { 42 | return [self mutableCopyWithZone:zone]; 43 | } 44 | 45 | - (instancetype) mutableCopyWithZone:(NSZone *)zone { 46 | @synchronized(self) { 47 | BranchMutableDictionary*copy = [[BranchMutableDictionary allocWithZone:zone] init]; 48 | [copy setDictionary:self]; 49 | return copy; 50 | } 51 | } 52 | 53 | - (NSUInteger) count { 54 | @synchronized(self) { 55 | return [_dictionary count]; 56 | } 57 | } 58 | 59 | - (id)objectForKey:(id)aKey { 60 | @synchronized(self) { 61 | return (aKey) ? [_dictionary objectForKey:aKey] : nil; 62 | } 63 | } 64 | 65 | - (NSEnumerator *)keyEnumerator { 66 | @synchronized(self) { 67 | return [_dictionary keyEnumerator]; 68 | } 69 | } 70 | 71 | - (void) setObject:(id)anObject forKey:(id<NSCopying>)aKey { 72 | @synchronized(self) { 73 | if (aKey == nil) { 74 | } else 75 | if (anObject == nil) 76 | [_dictionary removeObjectForKey:aKey]; 77 | else 78 | [_dictionary setObject:anObject forKey:aKey]; 79 | } 80 | } 81 | 82 | - (void) removeObjectForKey:(id)aKey { 83 | @synchronized(self) { 84 | if (aKey != nil) [_dictionary removeObjectForKey:aKey]; 85 | } 86 | } 87 | 88 | - (void)encodeWithCoder:(NSCoder *)aCoder { 89 | @synchronized(self) { 90 | [_dictionary encodeWithCoder:aCoder]; 91 | } 92 | } 93 | 94 | - (Class) classForCoder { 95 | return self.class; 96 | } 97 | 98 | @end 99 | -------------------------------------------------------------------------------- /Branch/BranchLinkProperties.m: -------------------------------------------------------------------------------- 1 | /** 2 | @file BranchLinkProperties.m 3 | @package Branch 4 | @brief Branch link properties: non-content properties that are associated with a link. 5 | 6 | @author Derrick Staten 7 | @date October 2015 8 | @copyright Copyright © 2015 Branch. All rights reserved. 9 | */ 10 | 11 | #import "BranchLinkProperties.h" 12 | 13 | @implementation BranchLinkProperties 14 | 15 | - (NSMutableDictionary*)controlParams { 16 | @synchronized(self) { 17 | if (!_controlParams) _controlParams = [[NSMutableDictionary alloc] init]; 18 | return _controlParams; 19 | } 20 | } 21 | 22 | + (instancetype)linkPropertiesWithDictionary:(NSDictionary *)dictionary { 23 | BranchLinkProperties *object = [[BranchLinkProperties alloc] init]; 24 | 25 | #define BNCWireFormatObjectFromDictionary 26 | #include "BNCWireFormat.h" 27 | 28 | addStringArray(tags, ~tags); 29 | addString(feature, ~feature); 30 | addString(alias, ~alias); 31 | addString(channel, ~channel); 32 | addString(stage, ~stage); 33 | addString(campaign, ~campaign); 34 | addInteger(matchDuration, ~duration); // TODO: encodes as $match_duration in iOS 35 | addInteger(linkType, ~type); // Is this right? 36 | 37 | NSMutableDictionary *controlParams = [[NSMutableDictionary alloc] init]; 38 | for (NSString*key in dictionary.allKeys) { 39 | if ([key hasPrefix:@"$"]) { 40 | controlParams[key] = dictionary[key]; 41 | } 42 | } 43 | object.controlParams = controlParams; 44 | 45 | return object; 46 | } 47 | 48 | - (NSDictionary*) dictionary { 49 | NSMutableDictionary*dictionary = [[NSMutableDictionary alloc] init]; 50 | 51 | #define BNCWireFormatDictionaryFromSelf 52 | #include "BNCWireFormat.h" 53 | 54 | addStringArray(tags, ~tags); 55 | addString(feature, ~feature); 56 | addString(alias, ~alias); 57 | addString(channel, ~channel); 58 | addString(stage, ~stage); 59 | addString(campaign, ~campaign); 60 | addInteger(matchDuration, ~duration); 61 | addInteger(linkType, ~type); 62 | 63 | //[dictionary addEntriesFromDictionary:self.controlParams]; 64 | 65 | return dictionary; 66 | } 67 | 68 | - (NSString *)description { 69 | return [NSString stringWithFormat: 70 | @"BranchLinkProperties | tags: %@ \n feature: %@ \n alias: %@ \n channel: %@ \n stage: %@ \n" 71 | " campaign: %@ \n matchDuration: %lu \n controlParams: %@", 72 | self.tags, self.feature, self.alias, self.channel, self.stage, self.campaign, 73 | (long)self.matchDuration, self.controlParams]; 74 | } 75 | 76 | @end 77 | -------------------------------------------------------------------------------- /Branch.xcodeproj/xcshareddata/xcschemes/Branch-macOS.xcscheme: -------------------------------------------------------------------------------- 1 | <?xml version="1.0" encoding="UTF-8"?> 2 | <Scheme 3 | LastUpgradeVersion = "1230" 4 | version = "1.3"> 5 | <BuildAction 6 | parallelizeBuildables = "YES" 7 | buildImplicitDependencies = "YES"> 8 | <BuildActionEntries> 9 | <BuildActionEntry 10 | buildForTesting = "YES" 11 | buildForRunning = "YES" 12 | buildForProfiling = "YES" 13 | buildForArchiving = "YES" 14 | buildForAnalyzing = "YES"> 15 | <BuildableReference 16 | BuildableIdentifier = "primary" 17 | BlueprintIdentifier = "4D54C07420A6560400C76496" 18 | BuildableName = "Branch.framework" 19 | BlueprintName = "Branch-macOS" 20 | ReferencedContainer = "container:Branch.xcodeproj"> 21 | </BuildableReference> 22 | </BuildActionEntry> 23 | </BuildActionEntries> 24 | </BuildAction> 25 | <TestAction 26 | buildConfiguration = "Debug" 27 | selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" 28 | selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" 29 | shouldUseLaunchSchemeArgsEnv = "YES"> 30 | <Testables> 31 | </Testables> 32 | </TestAction> 33 | <LaunchAction 34 | buildConfiguration = "Debug" 35 | selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" 36 | selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" 37 | launchStyle = "0" 38 | useCustomWorkingDirectory = "NO" 39 | ignoresPersistentStateOnLaunch = "NO" 40 | debugDocumentVersioning = "YES" 41 | debugServiceExtension = "internal" 42 | allowLocationSimulation = "YES"> 43 | </LaunchAction> 44 | <ProfileAction 45 | buildConfiguration = "Release" 46 | shouldUseLaunchSchemeArgsEnv = "YES" 47 | savedToolIdentifier = "" 48 | useCustomWorkingDirectory = "NO" 49 | debugDocumentVersioning = "YES"> 50 | <MacroExpansion> 51 | <BuildableReference 52 | BuildableIdentifier = "primary" 53 | BlueprintIdentifier = "4D54C07420A6560400C76496" 54 | BuildableName = "Branch.framework" 55 | BlueprintName = "Branch-macOS" 56 | ReferencedContainer = "container:Branch.xcodeproj"> 57 | </BuildableReference> 58 | </MacroExpansion> 59 | </ProfileAction> 60 | <AnalyzeAction 61 | buildConfiguration = "Debug"> 62 | </AnalyzeAction> 63 | <ArchiveAction 64 | buildConfiguration = "Release" 65 | revealArchiveInOrganizer = "YES"> 66 | </ArchiveAction> 67 | </Scheme> 68 | -------------------------------------------------------------------------------- /Branch.xcodeproj/xcshareddata/xcschemes/Branch-framework.xcscheme: -------------------------------------------------------------------------------- 1 | <?xml version="1.0" encoding="UTF-8"?> 2 | <Scheme 3 | LastUpgradeVersion = "1230" 4 | version = "1.3"> 5 | <BuildAction 6 | parallelizeBuildables = "YES" 7 | buildImplicitDependencies = "YES"> 8 | <BuildActionEntries> 9 | <BuildActionEntry 10 | buildForTesting = "YES" 11 | buildForRunning = "YES" 12 | buildForProfiling = "YES" 13 | buildForArchiving = "YES" 14 | buildForAnalyzing = "YES"> 15 | <BuildableReference 16 | BuildableIdentifier = "primary" 17 | BlueprintIdentifier = "5F93791125C3A107002AB515" 18 | BuildableName = "Branch-framework" 19 | BlueprintName = "Branch-framework" 20 | ReferencedContainer = "container:Branch.xcodeproj"> 21 | </BuildableReference> 22 | </BuildActionEntry> 23 | </BuildActionEntries> 24 | </BuildAction> 25 | <TestAction 26 | buildConfiguration = "Debug" 27 | selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" 28 | selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" 29 | shouldUseLaunchSchemeArgsEnv = "YES"> 30 | <Testables> 31 | </Testables> 32 | </TestAction> 33 | <LaunchAction 34 | buildConfiguration = "Debug" 35 | selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" 36 | selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" 37 | launchStyle = "0" 38 | useCustomWorkingDirectory = "NO" 39 | ignoresPersistentStateOnLaunch = "NO" 40 | debugDocumentVersioning = "YES" 41 | debugServiceExtension = "internal" 42 | allowLocationSimulation = "YES"> 43 | </LaunchAction> 44 | <ProfileAction 45 | buildConfiguration = "Release" 46 | shouldUseLaunchSchemeArgsEnv = "YES" 47 | savedToolIdentifier = "" 48 | useCustomWorkingDirectory = "NO" 49 | debugDocumentVersioning = "YES"> 50 | <MacroExpansion> 51 | <BuildableReference 52 | BuildableIdentifier = "primary" 53 | BlueprintIdentifier = "5F93791125C3A107002AB515" 54 | BuildableName = "Branch-framework" 55 | BlueprintName = "Branch-framework" 56 | ReferencedContainer = "container:Branch.xcodeproj"> 57 | </BuildableReference> 58 | </MacroExpansion> 59 | </ProfileAction> 60 | <AnalyzeAction 61 | buildConfiguration = "Debug"> 62 | </AnalyzeAction> 63 | <ArchiveAction 64 | buildConfiguration = "Release" 65 | revealArchiveInOrganizer = "YES"> 66 | </ArchiveAction> 67 | </Scheme> 68 | -------------------------------------------------------------------------------- /Branch/BranchSession.m: -------------------------------------------------------------------------------- 1 | /** 2 | @file BranchSession.m 3 | @package Branch 4 | @brief Attributes of the current Branch session. 5 | 6 | @author Edward 7 | @date 2018 8 | @copyright Copyright © 2018 Branch. All rights reserved. 9 | */ 10 | 11 | #import "BranchSession.h" 12 | #import "BNCLog.h" 13 | 14 | @interface BranchSession () 15 | @property (nonatomic, assign) BOOL matchGuaranteed; 16 | @property (nonatomic, strong) NSDate* clickTimestamp; 17 | @end 18 | 19 | @implementation BranchSession 20 | 21 | + (instancetype) sessionWithDictionary:(NSDictionary *)dictionary { 22 | BranchSession*object = [[BranchSession alloc] init]; 23 | 24 | #define BNCWireFormatObjectFromDictionary 25 | #include "BNCWireFormat.h" 26 | 27 | addString(sessionID, session_id); 28 | addString(userIdentityForDeveloper, identity); 29 | addString(randomizedDeviceToken, randomized_device_token); 30 | addString(randomizedBundleToken, randomized_bundle_token); 31 | addString(linkCreationURL, link); 32 | 33 | NSString*dataString = dictionary[@"data"]; 34 | if (!dataString) dataString = dictionary[@"referring_data"]; 35 | 36 | if (dataString) { 37 | NSData*dataData = [dataString dataUsingEncoding:NSUTF8StringEncoding]; 38 | if (dataData) { 39 | NSError*error = nil; 40 | object.data = [NSJSONSerialization JSONObjectWithData:dataData options:0 error:&error]; 41 | if (error) BNCLogError(@"Can't decode data: %@", error); 42 | NSDictionary*dictionary = object.data; 43 | addBoolean(isFirstSession, +is_first_session); 44 | addBoolean(isBranchURL, +clicked_branch_link); 45 | addBoolean(matchGuaranteed, +match_guaranteed); 46 | addURL(referringURL, ~referring_link); 47 | object.clickTimestamp = BNCDateFromWireFormatSeconds(dictionary[@"+click_timestamp"]); 48 | } 49 | } else { 50 | object.data = dictionary; 51 | } 52 | return object; 53 | } 54 | 55 | #include "BNCWireFormat.h" 56 | 57 | - (NSString*) description { 58 | return [NSString stringWithFormat: 59 | @"<%@ 0x%p isFirst: %@ isBranchURL: %@ sessionID: %@ referring: %@ identity: %@" 60 | " buo: %@ link: %@ items data: %@>", 61 | NSStringFromClass(self.class), 62 | (void*) self, 63 | BNCStringFromBool(self.isFirstSession), 64 | BNCStringFromBool(self.isBranchURL), 65 | self.sessionID, 66 | self.referringURL, 67 | self.randomizedBundleToken, 68 | self.linkContent, 69 | self.linkProperties, 70 | self.data]; 71 | } 72 | 73 | @end 74 | -------------------------------------------------------------------------------- /BranchTests/BNCLocalization.Test.m: -------------------------------------------------------------------------------- 1 | /** 2 | @file BNCLocalization.Test.m 3 | @package BranchTests 4 | @brief Branch string localization tests. 5 | 6 | @author Edward Smith 7 | @date July 2017 8 | @copyright Copyright © 2017 Branch. All rights reserved. 9 | */ 10 | 11 | #import "BNCTestCase.h" 12 | #import "BNCLocalization.h" 13 | 14 | @interface BNCLocalizationTest : BNCTestCase 15 | @end 16 | 17 | @implementation BNCLocalizationTest 18 | 19 | - (void) testLocalizationBasic { 20 | 21 | NSString *truth = nil; 22 | NSString *string = nil; 23 | [BNCLocalization shared].currentLanguage = @"en"; 24 | 25 | truth = @"Could not generate a URL."; 26 | string = BNCLocalizedString(truth); 27 | XCTAssertEqualObjects(string, truth); 28 | 29 | truth = @"This string is not in table: Should print a warning and return same string."; 30 | string = BNCLocalizedString(truth); 31 | XCTAssertEqualObjects(string, truth); 32 | 33 | #pragma clang diagnostic push 34 | #pragma clang diagnostic ignored "-Wall" 35 | // Nil input should return empty string output. 36 | truth = @""; 37 | string = BNCLocalizedString(nil); 38 | XCTAssertEqualObjects(string, truth); 39 | #pragma clang diagnostic pop 40 | 41 | // Test formatted language strings. 42 | truth = @"Test formatted language strings."; 43 | string = BNCLocalizedFormattedString(@"Test formatted %@ strings.", @"language"); 44 | XCTAssertEqualObjects(string, truth); 45 | 46 | // Test formatted language strings with format checking: 47 | truth = @"Test formatted language strings float 1.00."; 48 | string = BNCLocalizedFormattedString(@"Test formatted %@ strings float %1.2f.", @"language", 1.0); 49 | XCTAssertEqualObjects(string, truth); 50 | 51 | #pragma clang diagnostic push 52 | #pragma clang diagnostic ignored "-Wall" 53 | truth = @""; 54 | string = BNCLocalizedFormattedString(nil, 1.0); 55 | XCTAssertEqualObjects(string, truth); 56 | #pragma clang diagnostic pop 57 | } 58 | 59 | - (void) testLocalizationRussian { 60 | 61 | NSString *truth = nil; 62 | NSString *string = nil; 63 | [BNCLocalization shared].currentLanguage = @"ru"; 64 | 65 | string = @"Could not generate a URL."; 66 | truth = @"Не получилось сгенерировать URL."; 67 | string = BNCLocalizedString(string); 68 | XCTAssertEqualObjects(string, truth); 69 | } 70 | 71 | - (void) testApplicationLanguage { 72 | XCTAssertEqualObjects([BNCLocalization applicationLanguage], @"en"); 73 | } 74 | 75 | - (void) testSetWeirdLanguage { 76 | // App doesn't speak that. Default to english. 77 | [BNCLocalization shared].currentLanguage = @"UFOAlienSpeak"; 78 | XCTAssertEqualObjects([BNCLocalization shared].currentLanguage, @"en"); 79 | } 80 | 81 | @end 82 | -------------------------------------------------------------------------------- /Branch/BNCKeyChain.h: -------------------------------------------------------------------------------- 1 | /** 2 | @file BNCKeyChain.h 3 | @package Branch 4 | @brief Simple access routines for secure keychain storage. 5 | 6 | @author Edward Smith 7 | @date January 8, 2018 8 | @copyright Copyright © 2018 Branch. All rights reserved. 9 | */ 10 | 11 | #import "BranchHeader.h" 12 | 13 | NS_ASSUME_NONNULL_BEGIN 14 | 15 | @interface BNCKeyChain : NSObject 16 | 17 | - (instancetype) init __attribute__((unavailable("init is not available."))); 18 | 19 | - (instancetype) initWithSecurityAccessGroup:(NSString*)securityGroup NS_DESIGNATED_INITIALIZER; 20 | 21 | /** 22 | @brief Remove a value for a service and key. Optionally removes all keys and values for a service. 23 | 24 | @param service The name of the service under which to store the key. 25 | @param key The key to remove the value from. If `nil` is passed, all keys and values are removed for that service. 26 | @return Returns an `NSError` if an error occurs. 27 | */ 28 | - (NSError*_Nullable) removeValuesForService:(NSString*)service 29 | key:(NSString*_Nullable)key; 30 | 31 | /** 32 | @brief Returns a value for the passed service and key. 33 | 34 | @param service The name of the service that the value is stored under. 35 | @param key The key that the value is stored under. 36 | @param error If an error occurs, and `error` is a pointer to an error pointer, the error is returned here. 37 | @return Returns the value stored under `service` and `key`, or `nil` if none found. 38 | */ 39 | - (id _Nullable) retrieveValueForService:(NSString*)service 40 | key:(NSString*)key 41 | error:(NSError*_Nullable __autoreleasing *_Nullable)error; 42 | 43 | /** 44 | @brief Returns an array of all keys found for a service in the keychain. 45 | 46 | @param service The service name. 47 | @param error If an error occurs, the error is returned in `error` if it is not `NULL`. 48 | @return Returns an array of the items stored in the keychain or `nil`. 49 | */ 50 | - (NSArray<NSString*>*_Nullable) retrieveKeysWithService:(NSString*)service 51 | error:(NSError*_Nullable __autoreleasing *_Nullable)error; 52 | 53 | /** 54 | @brief Stores an item in the keychain. 55 | 56 | @param value The value to store. 57 | @param service The service name to store the item under. 58 | @param key The key to store the item under. 59 | @return Returns an error if an error occurs. 60 | */ 61 | - (NSError*_Nullable) storeValue:(id)value 62 | forService:(NSString*)service 63 | key:(NSString*)key; 64 | 65 | @property (atomic, copy, readonly) NSString* securityAccessGroup; 66 | @end 67 | 68 | NS_ASSUME_NONNULL_END 69 | -------------------------------------------------------------------------------- /Scripts/whichapp: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # whichapp - Prints details about a Mac app such as it's installed location, 4 | # bundleID, filename, and display name. 5 | # 6 | # Usage: whichapp [-bdfhps] [--] <appname> 7 | # 8 | # Exits code 0 if app is found, 1 otherwise. 9 | # 10 | # E.B. Smith - January 2014 11 | 12 | scriptname=whichapp 13 | 14 | usage () { 15 | cat <<USAGE 16 | 17 | $scriptname - Prints information about an OS X application. 18 | 19 | Usage: $scriptname [-abdfps] [--] <appname> 20 | 21 | Converts a common app name like 'textedit' to: 22 | 23 | * A canonical display name like 'TextEdit'. 24 | * A filename, like 'TextEdit.app'. 25 | * A path name, like '/Hard Drive/Applications/TextEdit.app' 26 | * A bundle ID, like 'com.apple.TextEdit' 27 | 28 | Options: 29 | 30 | -a Prints all application information. 31 | -b Prints the application's bundleID. 32 | -d Prints the application's display name. 33 | -f Prints the application's filename. 34 | -h Print this help. 35 | -p Prints the applcaition's full path (default). 36 | -s Silent mode. 37 | 38 | Exits with code 1 if <appname> is not found, 0 otherwise. 39 | 40 | USAGE 41 | exit 1 42 | } 43 | 44 | set -eu 45 | set -o pipefail 46 | 47 | appname="" 48 | bDisplayname=false 49 | bFilename=false 50 | bBundleID=false 51 | bPathname=false 52 | bSilent=false 53 | 54 | while getopts ":abdfhps" option; do 55 | case "$option" in 56 | a) bDisplayname=true 57 | bFilename=true 58 | bBundleID=true 59 | bPathname=true 60 | bSilent=false 61 | ;; 62 | b) bBundleID=true ;; 63 | d) bDisplayname=true ;; 64 | f) bFilename=true ;; 65 | h) usage ;; 66 | p) bPathname=true ;; 67 | s) bSilent=true ;; 68 | ?) echo "Unknown option '-$OPTARG'."; exit 1;; 69 | esac 70 | done 71 | 72 | appname="${@:$OPTIND:1}" 73 | if [[ ${#appname} == 0 ]]; then 74 | echo "An <appname> is required." 75 | usage 76 | fi 77 | 78 | if ! ($bBundleID || $bDisplayname || $bFilename || $bPathname) ; then 79 | bPathname=true 80 | fi 81 | 82 | if $bSilent ; then 83 | bBundleID=false 84 | bDisplayname=false 85 | bFilename=false 86 | bPathname=false 87 | fi 88 | 89 | query="(kMDItemFSName = \"*${appname}*\"cdw && kMDItemKind == \"Application\")" 90 | pathname=$(mdfind "${query}") 91 | count=$(wc -l <<<"${pathname}") 92 | if (( ${#pathname} == 0 )); then 93 | exit 1 94 | elif (( $count > 1 )); then 95 | echo "Too many results." 1>&2 96 | echo "${pathname}" 1>&2 97 | exit 1 98 | fi 99 | 100 | if $bDisplayname ; then 101 | echo `mdls -raw -name kMDItemDisplayName "${pathname}"` 102 | fi 103 | 104 | if $bFilename ; then 105 | echo `mdls -raw -name kMDItemFSName "${pathname}"` 106 | fi 107 | 108 | if $bBundleID ; then 109 | echo `mdls -raw -name kMDItemCFBundleIdentifier "${pathname}"` 110 | fi 111 | 112 | if $bPathname ; then 113 | echo "${pathname}" 114 | fi 115 | 116 | #DS al fine 117 | -------------------------------------------------------------------------------- /BranchTests/NSString+Branch.Test.m: -------------------------------------------------------------------------------- 1 | /** 2 | @file NSString+Branch.Test.m 3 | @package BranchTests 4 | @brief Tests for the NSString+Branch category. 5 | 6 | @author Edward Smith 7 | @date February 2017 8 | @copyright Copyright © 2017 Branch. All rights reserved. 9 | */ 10 | 11 | #import "BNCTestCase.h" 12 | #import "NSString+Branch.h" 13 | 14 | #define _countof(array) (sizeof(array)/sizeof(array[0])) 15 | 16 | @interface NSStringBranchTest : BNCTestCase 17 | @end 18 | 19 | @implementation NSStringBranchTest 20 | 21 | - (void) testMaskEqual { 22 | XCTAssertTrue([@"0123" bnc_isEqualToMaskedString:@"0123"]); 23 | XCTAssertFalse([@"0123" bnc_isEqualToMaskedString:@"012"]); 24 | XCTAssertFalse([@"0123" bnc_isEqualToMaskedString:@"01234"]); 25 | XCTAssertTrue([@"0123" bnc_isEqualToMaskedString:@"01*3"]); 26 | XCTAssertFalse([@"0123" bnc_isEqualToMaskedString:@"01*4"]); 27 | XCTAssertTrue([@"0123" bnc_isEqualToMaskedString:@"*123"]); 28 | XCTAssertTrue([@"0123" bnc_isEqualToMaskedString:@"012*"]); 29 | XCTAssertTrue([@"日本語123日本語" bnc_isEqualToMaskedString:@"日本語123日本語"]); 30 | XCTAssertFalse([@"日本語123日本語" bnc_isEqualToMaskedString:@"日本語1234本語"]); 31 | XCTAssertTrue([@"日本語123日本語" bnc_isEqualToMaskedString:@"日本語***日本語"]); 32 | XCTAssertTrue([@"日本語123日本語" bnc_isEqualToMaskedString:@"***123日本語"]); 33 | } 34 | 35 | - (void) testStringTruncatedAtNull { 36 | char bytes[] = "\x30\x31\x00\x32\x33\x34\x35\x36"; 37 | XCTAssert(sizeof(bytes) == 9); 38 | NSData *data = [NSData dataWithBytes:bytes length:sizeof(bytes)-1]; 39 | XCTAssert(data.length == 8); 40 | NSString *string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; 41 | XCTAssert(string.length == 8); 42 | string = [string bnc_stringTruncatedAtNull]; 43 | XCTAssert(string.length == 2); 44 | XCTAssertEqualObjects(string, @"01"); 45 | 46 | string = @""; 47 | NSString *test = @""; 48 | string = [string bnc_stringTruncatedAtNull]; 49 | XCTAssertEqualObjects(string, test); 50 | 51 | char byte2[] = "\x00\x31\x00\x32\x33\x34\x35\x36"; 52 | data = [NSData dataWithBytes:byte2 length:sizeof(byte2)]; 53 | string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; 54 | XCTAssert(string && string.length == 9); 55 | string = [string bnc_stringTruncatedAtNull]; 56 | XCTAssert(string && string.length == 0); 57 | 58 | string = @"No truncate"; 59 | test = @"No truncate"; 60 | string = [string bnc_stringTruncatedAtNull]; 61 | XCTAssertEqualObjects(string, test); 62 | } 63 | 64 | - (void) testContainsString { 65 | NSString *testString = @"I'm a good girl I am."; 66 | XCTAssertTrue([testString bnc_containsString:@"good girl"]); 67 | XCTAssertFalse([testString bnc_containsString:@"I'm a bad girl."]); 68 | XCTAssertFalse([testString bnc_containsString:nil]); 69 | XCTAssertTrue([testString bnc_containsString:testString]); 70 | XCTAssertFalse([testString bnc_containsString:@"I'm a good girl I am..."]); 71 | XCTAssertFalse([testString bnc_containsString:@""]); 72 | } 73 | 74 | @end 75 | -------------------------------------------------------------------------------- /Examples/TestBed-macOS/TestBed-macOSUITests/TestBedUINonBranchLinkTest.m: -------------------------------------------------------------------------------- 1 | // 2 | // TestBedUINonBranchLinkTest.m 3 | // TestBed-macOSUITests 4 | // 5 | // Created by Nidhi on 2/6/21. 6 | // Copyright © 2021 Branch. All rights reserved. 7 | // 8 | 9 | 10 | #import "TestBedUITest.h" 11 | #import "TestBedUIUtils.h" 12 | 13 | #define SLEEP_TIME_CLICK_BIG 3 14 | #define SLEEP_TIME_CLICK_SMALL 1 15 | 16 | @interface TestBedUINonBranchLinkTest : TestBedUITest 17 | 18 | @end 19 | 20 | @implementation TestBedUINonBranchLinkTest 21 | 22 | - (void)setUp { 23 | [super setUp]; 24 | } 25 | 26 | - (void)tearDown { 27 | [super tearDown]; 28 | } 29 | 30 | - (void)validateForNonBranchLink { 31 | 32 | NSMutableString *deepLinkDataString ; 33 | 34 | XCUIElement *testbedMacWindow = [[XCUIApplication alloc] init].windows[@"TestBed-Mac"]; 35 | XCUIElement *stateElementNext = testbedMacWindow.staticTexts[@"BranchDidStartSessionNotification"]; 36 | if ([stateElementNext waitForExistenceWithTimeout:15] != NO) { 37 | XCUIElement *dataTextView = [[[testbedMacWindow childrenMatchingType:XCUIElementTypeScrollView] elementBoundByIndex:0] childrenMatchingType:XCUIElementTypeTextView].element; 38 | deepLinkDataString = dataTextView.value; 39 | 40 | } else { 41 | XCTFail("BranchDidStartSessionNotification not received in 15 seconds"); 42 | } 43 | 44 | XCTAssertTrue([deepLinkDataString isNotEqualTo:@""]); 45 | XCTAssertTrue([deepLinkDataString containsString:@"\"+non_branch_link\" = \"testbed-mac://\""] ); 46 | } 47 | 48 | - (void)testNonBranchLink { 49 | 50 | XCUIApplication *testBedApp = [[XCUIApplication alloc] init]; 51 | if (testBedApp.state != XCUIApplicationStateNotRunning) 52 | [self terminateTestBed]; 53 | 54 | XCUIApplication *safariApp = [[XCUIApplication alloc] initWithBundleIdentifier:@"com.apple.Safari"]; 55 | [safariApp setLaunchArguments: @[[NSString stringWithFormat:@"%@%@", [[NSBundle mainBundle] bundlePath], @"/Contents/PlugIns/TestBed-macOSUITests.xctest/Contents/Resources/TestNonBranchLink.html"]]]; 56 | [safariApp launch]; 57 | sleep(SLEEP_TIME_CLICK_BIG); 58 | XCUIElement *testBedLink = [[safariApp.webViews descendantsMatchingType:XCUIElementTypeLink] elementBoundByIndex:0]; 59 | 60 | [testBedLink click]; 61 | sleep(SLEEP_TIME_CLICK_BIG); 62 | 63 | XCUIElement *toggleElement = [[safariApp descendantsMatchingType:XCUIElementTypeToggle] elementBoundByIndex:1 ]; 64 | if ([toggleElement waitForExistenceWithTimeout:12] != NO) { 65 | [toggleElement click]; 66 | } 67 | else { 68 | NSLog(@"Toggle Element(TestBed Launch Confirmation Dialog) Not Found"); 69 | } 70 | 71 | if ([[[XCUIApplication alloc] init] waitForExistenceWithTimeout:15] != NO) { 72 | self.appLaunched = TRUE; 73 | [self validateForNonBranchLink]; 74 | [safariApp activate]; 75 | [safariApp typeKey:@"W" modifierFlags:XCUIKeyModifierShift|XCUIKeyModifierCommand|XCUIKeyModifierOption]; 76 | 77 | } else { 78 | XCTFail("Application not launched"); 79 | } 80 | } 81 | 82 | @end 83 | -------------------------------------------------------------------------------- /BranchTests/BNCThreads.Test.m: -------------------------------------------------------------------------------- 1 | /** 2 | @file BNCThreads.Test.m 3 | @package BranchTests 4 | @brief Tests for BNCThreads. 5 | 6 | @author Edward Smith 7 | @date May 2018 8 | @copyright Copyright © 2018 Branch. All rights reserved. 9 | */ 10 | 11 | #import "BNCTestCase.h" 12 | #import "BNCThreads.h" 13 | 14 | @interface BNCThreadsTest : BNCTestCase 15 | @end 16 | 17 | @implementation BNCThreadsTest 18 | 19 | - (void) testTimeConversion { 20 | uint64_t nsec = BNCNanoSecondsFromTimeInterval(1.25); 21 | XCTAssert(nsec == 1250000000ull); 22 | 23 | XCTestExpectation*expectation = 24 | [self expectationWithDescription:@"BNCAfterSecondsPerformBlockOnMainThread"]; 25 | NSDate*date = [NSDate date]; 26 | BNCAfterSecondsPerformBlockOnMainThread(1.25, ^{ 27 | NSTimeInterval te = [date timeIntervalSinceNow]; 28 | XCTAssertTrue(te + 1.25 < 0.2000); 29 | [expectation fulfill]; 30 | }); 31 | [self awaitExpectations]; 32 | } 33 | 34 | - (void) testAsyncQueue { 35 | XCTestExpectation*expectation = 36 | [self expectationWithDescription:@"BNCPerformBlockOnMainThreadAsync"]; 37 | BNCPerformBlockOnMainThreadAsync(^{ 38 | XCTAssertTrue([NSThread isMainThread]); 39 | [expectation fulfill]; 40 | }); 41 | [self awaitExpectations]; 42 | } 43 | 44 | - (void) testSyncQueue { 45 | XCTestExpectation*expectation = 46 | [self expectationWithDescription:@"BNCPerformBlockOnMainThreadSync"]; 47 | XCTAssertTrue([NSThread isMainThread]); 48 | BNCPerformBlockOnMainThreadSync(^{ 49 | XCTAssertTrue([NSThread isMainThread]); 50 | [expectation fulfill]; 51 | }); 52 | [self awaitExpectations]; 53 | } 54 | 55 | - (void) testSleep { 56 | NSDate*date = [NSDate date]; 57 | BNCSleepForTimeInterval(0.125); 58 | NSTimeInterval delta = [date timeIntervalSinceNow]; 59 | XCTAssertTrue(delta < -0.125 && delta > -0.200); 60 | } 61 | 62 | - (void) testPerforms { 63 | NSDate*date = [NSDate date]; 64 | XCTestExpectation*expectation = 65 | [self expectationWithDescription:@"BNCPerformBlockOnMainThreadSync"]; 66 | BNCAfterSecondsPerformBlockOnMainThread(1.0, ^ { 67 | XCTAssertTrue([NSThread isMainThread]); 68 | [expectation fulfill]; 69 | }); 70 | [self awaitExpectations]; 71 | NSTimeInterval t = [date timeIntervalSinceNow]; 72 | XCTAssert(t >= -1.5 && t < -1.0); 73 | } 74 | 75 | - (void) testBNCPerformBlockAsync { 76 | XCTestExpectation*expectation = [self expectationWithDescription:@"BNCPerformBlockAsync"]; 77 | BNCPerformBlockAsync( ^ { 78 | [expectation fulfill]; 79 | }); 80 | [self awaitExpectations]; 81 | } 82 | 83 | - (void) testBNCAfterSecondsPerformBlock { 84 | NSDate*date = [NSDate date]; 85 | XCTestExpectation*expectation = 86 | [self expectationWithDescription:@"BNCAfterSecondsPerformBlock"]; 87 | BNCAfterSecondsPerformBlock(1.0, ^ { 88 | [expectation fulfill]; 89 | }); 90 | [self awaitExpectations]; 91 | NSTimeInterval t = [date timeIntervalSinceNow]; 92 | XCTAssert(t >= -1.25 && t < -1.0); 93 | } 94 | 95 | @end 96 | -------------------------------------------------------------------------------- /Examples/TestBed-macOS/TestBed-macOSUITests/TestBedUIInstallOpenTest.m: -------------------------------------------------------------------------------- 1 | // 2 | // TestBedUIInstallOpenTest.m 3 | // TestBed-macOSUITests 4 | // 5 | // Created by Nidhi on 11/3/20. 6 | // Copyright © 2020 Branch. All rights reserved. 7 | // 8 | 9 | #import "TestBedUITest.h" 10 | #import "TestBedUIUtils.h" 11 | 12 | @interface TestBedUIInstallOpenTest : TestBedUITest 13 | 14 | @end 15 | 16 | @implementation TestBedUIInstallOpenTest 17 | 18 | - (void)setUp { 19 | [super setUp]; 20 | } 21 | 22 | - (void)tearDown { 23 | [super tearDown]; 24 | } 25 | 26 | - (void)testInstallAndOpen { 27 | 28 | __block NSString *randomizedBundleToken; 29 | __block NSString *randomizedDeviceToken; 30 | 31 | [TestBedUIUtils deleteSettingsFiles]; 32 | 33 | if (self.trackingState == TRACKING_DISABLED) { 34 | [self enableTracking]; 35 | } 36 | 37 | XCTWaiterResult result = [self launchAppAndWaitForSessionStart]; 38 | 39 | [XCTContext runActivityNamed:@"InstallAPI" block:^(id<XCTActivity> activity){ 40 | 41 | if (result == XCTWaiterResultCompleted) { 42 | 43 | XCTAssertTrue([[self serverRequestString] containsString:@"/v1/install"]); 44 | 45 | NSDictionary *serverResponseDictionary = [ TestBedUIUtils dictionaryFromString:[self serverResponseString]]; 46 | XCTAssertNotNil([serverResponseDictionary valueForKey:@"randomized_bundle_token"]); 47 | XCTAssertNotNil([serverResponseDictionary valueForKey:@"randomized_device_token"]); 48 | 49 | randomizedBundleToken = [serverResponseDictionary objectForKey:@"randomized_bundle_token"]; 50 | randomizedDeviceToken = [serverResponseDictionary objectForKey:@"randomized_device_token"]; 51 | 52 | [activity addAttachment:[XCTAttachment attachmentWithScreenshot:[[XCUIScreen mainScreen] screenshot]]]; 53 | [self terminateTestBed]; 54 | self.appLaunched = FALSE; 55 | } 56 | else { 57 | XCTFail("App Launch / Session Start Failed."); 58 | } 59 | }]; 60 | 61 | result = [self launchAppAndWaitForSessionStart]; 62 | 63 | [XCTContext runActivityNamed:@"OpenAPI" block:^(id<XCTActivity> activity){ 64 | if (result == XCTWaiterResultCompleted) { 65 | 66 | XCTAssertTrue([[self serverRequestString] containsString:@"/v1/open"]); 67 | 68 | NSDictionary *serverRequestDictionary = [TestBedUIUtils dictionaryFromString:[self serverRequestString]]; 69 | XCTAssertNotNil([serverRequestDictionary valueForKey:@"randomized_bundle_token"]); 70 | XCTAssertEqualObjects([serverRequestDictionary valueForKey:@"randomized_bundle_token"], randomizedBundleToken); 71 | 72 | XCTAssertNotNil([serverRequestDictionary valueForKey:@"randomized_device_token"]); 73 | XCTAssertEqualObjects([serverRequestDictionary valueForKey:@"randomized_device_token"], randomizedDeviceToken); 74 | 75 | XCTAssertTrue([[self serverResponseString] containsString:@"Status 200"]); 76 | } 77 | else { 78 | XCTFail("App Launch / Session Start Failed."); 79 | } 80 | }]; 81 | 82 | } 83 | 84 | @end 85 | -------------------------------------------------------------------------------- /Branch/BranchNetworkServiceProtocol.h: -------------------------------------------------------------------------------- 1 | /** 2 | @file BranchNetworkServiceProtocol.h 3 | @package Branch 4 | @brief A networking protocol contract to an abstract underlying network class. 5 | 6 | @author Edward Smith 7 | @date May 30, 2017 8 | @copyright Copyright © 2017 Branch. All rights reserved. 9 | */ 10 | 11 | #import "BranchHeader.h" 12 | 13 | NS_ASSUME_NONNULL_BEGIN 14 | 15 | 16 | #ifndef BNCNetworkOperationProtocol_h 17 | #define BNCNetworkOperationProtocol_h 18 | 19 | #pragma mark BNCNetworkOperationProtocol 20 | 21 | /** 22 | @name The `BNCNetworkServiceProtocol` and `BNCNetworkOperationProtocol` protocols. 23 | 24 | @discussion 25 | The protocols `BNCNetworkServiceProtocol` and `BNCNetworkOperationProtocol` describe the methods 26 | needed to create a drop in replacement for the standard Branch SDK networking. 27 | 28 | See `Branch-SDK/Network/BNCNetworkService.h` and `Branch-SDK/Network/BNCNetworkService.m` for a 29 | concrete example of how to implement the BNCNetworkServiceProtocol and BNCNetworkOperationProtocol 30 | protocols. 31 | 32 | Usage 33 | ----- 34 | 35 | 1. Create your own network service class that follows the `BNCNetworkServiceProtocol`. 36 | The `new` and `networkOperationWithURLRequest:completion:` methods are required. The 37 | others are optional. 38 | 39 | 2. Create your own network operation class that follows the `BNCNetworkOperationProtocol`. 40 | The `start` method is required, as are all the getters for request, response, error, and date 41 | data items. 42 | 43 | 3. In your app delegate, set your network class by calling `[Branch setNetworkServiceClass:]` with 44 | your network class as a parameter. This method must be called before initializing the Branch 45 | shared object. 46 | 47 | */ 48 | @protocol BNCNetworkOperationProtocol <NSObject> 49 | 50 | /// The initial NSMutableURLRequest. 51 | @required 52 | @property (readonly) NSURLRequest *request; 53 | 54 | /// The response code from the server. 55 | @required 56 | @property (readonly) NSInteger HTTPStatusCode; 57 | 58 | /// The data from the server. 59 | @required 60 | @property (readonly) NSData*_Nullable responseData; 61 | 62 | /// Any errors that occurred during the request. 63 | @required 64 | @property (readonly) NSError*_Nullable error; 65 | 66 | /// Starts the network operation. 67 | @required 68 | - (void) start; 69 | 70 | /// Cancels a queued or in progress network operation. 71 | @optional 72 | - (void) cancel; 73 | 74 | @end 75 | 76 | #pragma mark - BNCNetworkServiceProtocol 77 | 78 | /** 79 | The `BNCNetworkServiceProtocol` defines a network service that handles a queue of network 80 | operations. 81 | */ 82 | @protocol BNCNetworkServiceProtocol <NSObject> 83 | 84 | /// Creates and returns a new network service. 85 | @required 86 | - (id<BNCNetworkServiceProtocol>) init; 87 | 88 | /// Cancel all current and queued network operations. 89 | @optional 90 | - (void) cancelAllOperations; 91 | 92 | /// Create and return a new network operation object. The network operation is not started until 93 | /// `[operation start]` is called. 94 | @required 95 | - (id<BNCNetworkOperationProtocol>) networkOperationWithURLRequest:(NSMutableURLRequest*)request 96 | completion:(void (^)(id<BNCNetworkOperationProtocol>operation))completion; 97 | 98 | @end 99 | 100 | #endif 101 | 102 | NS_ASSUME_NONNULL_END 103 | -------------------------------------------------------------------------------- /BranchTests/BNCTestNetworkService.m: -------------------------------------------------------------------------------- 1 | /** 2 | @file BNCTestNetworkService.m 3 | @package BranchTests 4 | @brief A class for mocking network service calls. 5 | 6 | @author Edward 7 | @date 2018 8 | @copyright Copyright © 2018 Branch. All rights reserved. 9 | */ 10 | 11 | #import "BNCTestNetworkService.h" 12 | #import "BNCThreads.h" 13 | 14 | @interface BNCTestNetworkService () 15 | - (void) startOperation:(BNCTestNetworkOperation*)operation; 16 | @end 17 | 18 | #pragma mark BNCTestNetworkOperation 19 | 20 | @interface BNCTestNetworkOperation () 21 | @property (strong) BNCTestNetworkService*networkService; 22 | @property (copy, nullable) void (^completionBlock)(BNCTestNetworkOperation*); 23 | @end 24 | 25 | @implementation BNCTestNetworkOperation 26 | 27 | - (void) start { 28 | [self.networkService startOperation:self]; 29 | } 30 | 31 | - (void) cancel { 32 | } 33 | 34 | @end 35 | 36 | #pragma mark - BNCTestNetworkService 37 | 38 | @implementation BNCTestNetworkService 39 | 40 | static id<BNCNetworkOperationProtocol>(^_requestHandler)(NSMutableURLRequest*request); 41 | 42 | + (void) setRequestHandler:(id<BNCNetworkOperationProtocol>_Nonnull(^)(NSMutableURLRequest*_Nonnull))requestHandler { 43 | @synchronized(self) { 44 | _requestHandler = [requestHandler copy]; 45 | } 46 | } 47 | 48 | + (id<BNCNetworkOperationProtocol> _Nonnull (^)(NSMutableURLRequest * _Nonnull))requestHandler { 49 | @synchronized(self) { 50 | return [_requestHandler copy]; 51 | } 52 | } 53 | 54 | + (NSMutableDictionary*) mutableDictionaryFromRequest:(NSURLRequest*)request { 55 | NSData*data = request.HTTPBody; 56 | if (!data) return nil; 57 | NSMutableDictionary *dictionary = [NSJSONSerialization JSONObjectWithData:data 58 | options:NSJSONReadingMutableContainers error:nil]; 59 | return dictionary; 60 | } 61 | 62 | + (id<BNCNetworkOperationProtocol>) operationWithRequest:(NSMutableURLRequest*)request 63 | response:(NSString*)responseString { 64 | BNCTestNetworkOperation*operation = [[BNCTestNetworkOperation alloc] init]; 65 | operation.request = request; 66 | operation.HTTPStatusCode = 200; 67 | operation.responseData = [responseString dataUsingEncoding:NSUTF8StringEncoding]; 68 | return operation; 69 | } 70 | 71 | #pragma mark - Protocol Methods 72 | 73 | - (id<BNCNetworkOperationProtocol>) networkOperationWithURLRequest:(NSMutableURLRequest*)request 74 | completion:(void (^)(id<BNCNetworkOperationProtocol>operation))completion { 75 | id<BNCNetworkOperationProtocol>operation = nil; 76 | if (self.class.requestHandler == nil) { 77 | // [NSException raise:NSInternalInconsistencyException 78 | // format:@"%@ requestHandler not set!", NSStringFromClass(self.class)]; 79 | operation = [self.class operationWithRequest:request response:@"{}"]; 80 | } else { 81 | operation = self.class.requestHandler(request); 82 | } 83 | ((BNCTestNetworkOperation*)operation).networkService = self; 84 | ((BNCTestNetworkOperation*)operation).completionBlock = completion; 85 | return operation; 86 | } 87 | 88 | - (void) startOperation:(BNCTestNetworkOperation*)operation { 89 | operation.networkService = self; 90 | // operation.startDate = [NSDate date]; 91 | // operation.timeoutDate = [operation.startDate dateByAddingTimeInterval:operation.request.timeoutInterval]; 92 | BNCAfterSecondsPerformBlockOnMainThread(0.010, ^{ 93 | if (operation.completionBlock) 94 | operation.completionBlock(operation); 95 | }); 96 | } 97 | 98 | @end 99 | -------------------------------------------------------------------------------- /Branch.xcodeproj/xcshareddata/xcschemes/BranchTestHost.xcscheme: -------------------------------------------------------------------------------- 1 | <?xml version="1.0" encoding="UTF-8"?> 2 | <Scheme 3 | LastUpgradeVersion = "1230" 4 | version = "1.3"> 5 | <BuildAction 6 | parallelizeBuildables = "YES" 7 | buildImplicitDependencies = "YES"> 8 | <BuildActionEntries> 9 | <BuildActionEntry 10 | buildForTesting = "YES" 11 | buildForRunning = "YES" 12 | buildForProfiling = "YES" 13 | buildForArchiving = "YES" 14 | buildForAnalyzing = "YES"> 15 | <BuildableReference 16 | BuildableIdentifier = "primary" 17 | BlueprintIdentifier = "5F2FDA32249D3CBF0061D571" 18 | BuildableName = "BranchTestHost.app" 19 | BlueprintName = "BranchTestHost" 20 | ReferencedContainer = "container:Branch.xcodeproj"> 21 | </BuildableReference> 22 | </BuildActionEntry> 23 | </BuildActionEntries> 24 | </BuildAction> 25 | <TestAction 26 | buildConfiguration = "Debug" 27 | selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" 28 | selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" 29 | shouldUseLaunchSchemeArgsEnv = "YES"> 30 | <Testables> 31 | <TestableReference 32 | skipped = "NO"> 33 | <BuildableReference 34 | BuildableIdentifier = "primary" 35 | BlueprintIdentifier = "5F78ED8E249C317400E313B9" 36 | BuildableName = "BranchTests.xctest" 37 | BlueprintName = "BranchTests" 38 | ReferencedContainer = "container:Branch.xcodeproj"> 39 | </BuildableReference> 40 | </TestableReference> 41 | </Testables> 42 | </TestAction> 43 | <LaunchAction 44 | buildConfiguration = "Debug" 45 | selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" 46 | selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" 47 | launchStyle = "0" 48 | useCustomWorkingDirectory = "NO" 49 | ignoresPersistentStateOnLaunch = "NO" 50 | debugDocumentVersioning = "YES" 51 | debugServiceExtension = "internal" 52 | allowLocationSimulation = "YES"> 53 | <BuildableProductRunnable 54 | runnableDebuggingMode = "0"> 55 | <BuildableReference 56 | BuildableIdentifier = "primary" 57 | BlueprintIdentifier = "5F2FDA32249D3CBF0061D571" 58 | BuildableName = "BranchTestHost.app" 59 | BlueprintName = "BranchTestHost" 60 | ReferencedContainer = "container:Branch.xcodeproj"> 61 | </BuildableReference> 62 | </BuildableProductRunnable> 63 | </LaunchAction> 64 | <ProfileAction 65 | buildConfiguration = "Release" 66 | shouldUseLaunchSchemeArgsEnv = "YES" 67 | savedToolIdentifier = "" 68 | useCustomWorkingDirectory = "NO" 69 | debugDocumentVersioning = "YES"> 70 | <BuildableProductRunnable 71 | runnableDebuggingMode = "0"> 72 | <BuildableReference 73 | BuildableIdentifier = "primary" 74 | BlueprintIdentifier = "5F2FDA32249D3CBF0061D571" 75 | BuildableName = "BranchTestHost.app" 76 | BlueprintName = "BranchTestHost" 77 | ReferencedContainer = "container:Branch.xcodeproj"> 78 | </BuildableReference> 79 | </BuildableProductRunnable> 80 | </ProfileAction> 81 | <AnalyzeAction 82 | buildConfiguration = "Debug"> 83 | </AnalyzeAction> 84 | <ArchiveAction 85 | buildConfiguration = "Release" 86 | revealArchiveInOrganizer = "YES"> 87 | </ArchiveAction> 88 | </Scheme> 89 | -------------------------------------------------------------------------------- /Branch/BNCApplication.h: -------------------------------------------------------------------------------- 1 | /** 2 | @file BNCApplication.h 3 | @package Branch 4 | @brief Current application and extension info. 5 | 6 | @author Edward Smith 7 | @date January 8, 2018 8 | @copyright Copyright © 2018 Branch. All rights reserved. 9 | */ 10 | 11 | #import "BranchHeader.h" 12 | 13 | NS_ASSUME_NONNULL_BEGIN 14 | 15 | typedef NS_ENUM(NSInteger, BNCApplicationUpdateState) { 16 | BNCApplicationUpdateStateInstall = 0, //!< Application was recently installed. 17 | BNCApplicationUpdateStateNonUpdate = 1, //!< Application was neither newly installed nor updated. 18 | BNCApplicationUpdateStateUpdate = 2, //!< Application was recently updated. 19 | 20 | BNCApplicationUpdateStateError = 3, //!< Error determining update state. 21 | BNCApplicationUpdateStateReinstall = 4, //!< App was re-installed. 22 | }; 23 | 24 | @interface BNCApplication : NSObject 25 | 26 | /// A reference to the current running application. 27 | + (BNCApplication*_Nonnull) currentApplication; 28 | 29 | /// The bundle identifier of the current 30 | @property (atomic, readonly) NSString*_Nullable bundleID; 31 | 32 | /// The team that was for code signing. 33 | @property (atomic, readonly) NSString*_Nullable teamID; 34 | 35 | /// The bundle display name from the info plist. 36 | @property (atomic, readonly) NSString*_Nullable displayName; 37 | 38 | /// The bundle short display name from the info plist. 39 | @property (atomic, readonly) NSString*_Nullable shortDisplayName; 40 | 41 | /// The short version ID as is typically shown to the user, like in iTunes (CFBundleShortVersionString). 42 | @property (atomic, readonly) NSString*_Nullable displayVersionString; 43 | 44 | /// The version ID that developers use (CFBundleVersion). 45 | @property (atomic, readonly) NSString*_Nullable versionString; 46 | 47 | /// The creation date of the current executable. 48 | @property (atomic, readonly) NSDate*_Nullable currentBuildDate; 49 | 50 | /// Previous value of the creation date of the current executable, if available. 51 | @property (atomic, readonly) NSDate*_Nullable previousAppBuildDate; 52 | 53 | /// The creating date of the exectuble the first time it was recorded by Branch. 54 | @property (atomic, readonly) NSDate*_Nullable firstInstallBuildDate; 55 | 56 | /// The date this app was installed on this device. 57 | @property (atomic, readonly) NSDate*_Nullable currentInstallDate; 58 | 59 | /// The date this app was first installed on this device. 60 | @property (atomic, readonly) NSDate*_Nullable firstInstallDate; 61 | 62 | /// The update state off the application. 63 | @property (atomic, readonly) BNCApplicationUpdateState updateState; 64 | 65 | /// YES if running as an application. 66 | @property (atomic, readonly) BOOL isApplication; 67 | 68 | /// YES if running as an application extension. 69 | @property (atomic, readonly) BOOL isApplicationExtension; 70 | 71 | /// The app extension type or `nil` for a full application. 72 | @property (atomic, readonly) NSString*_Nullable extensionType; 73 | 74 | /// The Branch extension type. 75 | @property (atomic, readonly) NSString* branchExtensionType; 76 | 77 | /// The default URL scheme for the app as found in the app's Info.plist. 78 | @property (atomic, readonly) NSString*_Nullable defaultURLScheme; 79 | 80 | /// The unique application identifier. Typically this is `teamID.bundleID`: XYZ123.com.company.app. 81 | @property (atomic, readonly) NSString*_Nullable applicationID; 82 | 83 | /// The push notification environment. Usually `development` or `production` or `nil`. 84 | @property (atomic, readonly) NSString*_Nullable pushNotificationEnvironment; 85 | 86 | /// The keychain access groups from the entitlements. 87 | @property (atomic, readonly) NSArray<NSString*>*_Nullable keychainAccessGroups; 88 | 89 | /// The associated domains from the entitlements. 90 | @property (atomic, readonly) NSArray<NSString*>*_Nullable associatedDomains; 91 | @end 92 | 93 | NS_ASSUME_NONNULL_END 94 | -------------------------------------------------------------------------------- /BranchTests/BNCApplication.Test.m: -------------------------------------------------------------------------------- 1 | /** 2 | @file BNCApplication.Test.m 3 | @package BranchTests 4 | @brief Tests for BNCApplication. 5 | 6 | @author Edward Smith 7 | @date January 2018 8 | @copyright Copyright © 2018 Branch. All rights reserved. 9 | */ 10 | 11 | #import "BNCTestCase.h" 12 | #import "BNCApplication.h" 13 | #import "BNCKeyChain.h" 14 | #import "BranchMainClass.h" 15 | #import "BranchMainClass+Private.h" 16 | 17 | @interface BNCApplication (BNCTestCase) 18 | + (BNCApplication *) createCurrentApplication; 19 | @end 20 | 21 | #pragma mark - BNCApplicationTest 22 | 23 | @interface BNCApplicationTest : BNCTestCase 24 | @end 25 | 26 | @implementation BNCApplicationTest 27 | 28 | - (void)testIsApplication { 29 | XCTAssertTrue([BNCApplication currentApplication].isApplication); 30 | } 31 | 32 | - (void)testApplication { 33 | BNCApplication *application = [BNCApplication currentApplication]; 34 | if (!application.isApplication) { 35 | return; 36 | } 37 | NSDictionary *info = [NSBundle mainBundle].infoDictionary; 38 | XCTAssertEqualObjects(application.bundleID, info[@"CFBundleIdentifier"]); 39 | XCTAssertEqualObjects(application.displayName, info[@"CFBundleName"]); 40 | XCTAssertEqualObjects(application.shortDisplayName, info[@"CFBundleName"]); 41 | XCTAssertEqualObjects(application.displayVersionString, info[@"CFBundleShortVersionString"]); 42 | XCTAssertEqualObjects(application.versionString, info[@"CFBundleVersion"]); 43 | 44 | XCTAssert(application.extensionType == nil); 45 | XCTAssert([application.branchExtensionType isEqualToString:@"FULL_APP"]); 46 | 47 | XCTAssert(application.pushNotificationEnvironment == nil); 48 | 49 | // TODO: These only exist when integrated into a full app, make them part of an integration test 50 | //XCTAssert(application.defaultURLScheme.length > 0); 51 | //XCTAssert(application.applicationID.length > 0); 52 | //XCTAssert(application.teamID.length > 0); 53 | //XCTAssert(application.keychainAccessGroups.count > 0); 54 | 55 | XCTAssert(application.associatedDomains.count == 0); 56 | } 57 | 58 | // TODO: unit tests should not be order dependent, refactor this test 59 | // These are methods are numbered so they run in order: 60 | //- (void) testAppUpdate1Basic { 61 | // BNCApplication *application = [BNCApplication currentApplication]; 62 | // if (!application.isApplication) { 63 | // return; 64 | // } 65 | // 66 | // // 67 | // // App dates. Not a great test but tests basic function: 68 | // // 69 | // NSTimeInterval const kOneYearAgo = -365.0 * 24.0 * 60.0 * 60.0; 70 | // 71 | // XCTAssertTrue(application.firstInstallDate && [application.firstInstallDate timeIntervalSinceNow] > kOneYearAgo); 72 | // XCTAssertTrue(application.firstInstallBuildDate && [application.firstInstallBuildDate timeIntervalSinceNow] > kOneYearAgo); 73 | // XCTAssertTrue(application.currentInstallDate && [application.currentInstallDate timeIntervalSinceNow] > kOneYearAgo); 74 | // XCTAssertTrue(application.currentBuildDate && [application.currentBuildDate timeIntervalSinceNow] > kOneYearAgo); 75 | // 76 | // NSString*const kBranchKeychainService = @"BranchKeychainService"; 77 | // NSString*const kBranchKeychainFirstBuildKey = @"BranchKeychainFirstBuild"; 78 | // NSString*const kBranchKeychainFirstInstalldKey = @"BranchKeychainFirstInstall"; 79 | // 80 | // NSString*securityGroup = application.applicationID; 81 | // BNCKeyChain*keychain = [[BNCKeyChain alloc] initWithSecurityAccessGroup:securityGroup]; 82 | // 83 | // NSDate *firstBuildDate = [keychain retrieveValueForService:kBranchKeychainService key:kBranchKeychainFirstBuildKey error:nil]; 84 | // XCTAssertEqualObjects(application.firstInstallBuildDate, firstBuildDate); 85 | // 86 | // NSDate *firstInstallDate = [keychain retrieveValueForService:kBranchKeychainService key:kBranchKeychainFirstInstalldKey error:nil]; 87 | // XCTAssertEqualObjects(application.firstInstallDate, firstInstallDate); 88 | //} 89 | 90 | @end 91 | -------------------------------------------------------------------------------- /Examples/TestBed-macOS/TestBed-macOS/APPV2EventSelectionWindow.xib: -------------------------------------------------------------------------------- 1 | <?xml version="1.0" encoding="UTF-8"?> 2 | <document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="17156" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct"> 3 | <dependencies> 4 | <deployment identifier="macosx"/> 5 | <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="17156"/> 6 | <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> 7 | </dependencies> 8 | <objects> 9 | <customObject id="-2" userLabel="File's Owner" customClass="APPViewController"> 10 | <connections> 11 | <outlet property="v2EventsComboBox" destination="18I-RU-gA2" id="wRt-cD-krq"/> 12 | <outlet property="v2EventsSelectionWindow" destination="QvC-M9-y7g" id="Oki-ty-z61"/> 13 | </connections> 14 | </customObject> 15 | <customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/> 16 | <customObject id="-3" userLabel="Application" customClass="NSObject"/> 17 | <window title="Window" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" id="QvC-M9-y7g"> 18 | <windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES"/> 19 | <windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/> 20 | <rect key="contentRect" x="196" y="240" width="348" height="150"/> 21 | <rect key="screenRect" x="0.0" y="0.0" width="1440" height="877"/> 22 | <view key="contentView" wantsLayer="YES" id="EiT-Mj-1SZ"> 23 | <rect key="frame" x="0.0" y="0.0" width="348" height="150"/> 24 | <autoresizingMask key="autoresizingMask"/> 25 | <subviews> 26 | <button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Rqp-yu-kiL"> 27 | <rect key="frame" x="245" y="13" width="89" height="32"/> 28 | <autoresizingMask key="autoresizingMask"/> 29 | <buttonCell key="cell" type="push" title="Send" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="NO0-O6-V99"> 30 | <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/> 31 | <font key="font" metaFont="system"/> 32 | </buttonCell> 33 | <connections> 34 | <action selector="closev2EventsSelectionWindow:" target="-2" id="J3g-OC-MaC"/> 35 | </connections> 36 | </button> 37 | <comboBox verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="18I-RU-gA2"> 38 | <rect key="frame" x="20" y="72" width="311" height="25"/> 39 | <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/> 40 | <comboBoxCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" placeholderString="Select V2 Event" drawsBackground="YES" completes="NO" numberOfVisibleItems="5" id="pHz-Og-rYr"> 41 | <font key="font" metaFont="system"/> 42 | <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/> 43 | <color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/> 44 | </comboBoxCell> 45 | </comboBox> 46 | </subviews> 47 | </view> 48 | <point key="canvasLocation" x="43" y="109"/> 49 | </window> 50 | </objects> 51 | </document> 52 | -------------------------------------------------------------------------------- /.github/workflows/sync-readme-changelog.yml: -------------------------------------------------------------------------------- 1 | name: Update Version History on Readme 2 | 3 | on: 4 | release: 5 | types: [published] 6 | 7 | jobs: 8 | update-changelog: 9 | runs-on: ubuntu-latest 10 | 11 | steps: 12 | - name: Format and publish release notes to version history doc 13 | id: update 14 | run: | 15 | # Get release name, body, and date from the release event 16 | release_name="${{ github.event.release.tag_name }}" 17 | release_body="${{ github.event.release.body }}" 18 | release_date=$(date -d "${{ github.event.release.published_at }}" +"%Y-%B-%d") 19 | # Format release notes 20 | formatted_notes="## v$release_name\n\n**($release_date)**\n\n$release_body" 21 | 22 | # Get existing version history page 23 | existing_content=$(curl --request GET \ 24 | --url https://dash.readme.com/api/v1/docs/mac-os-version-history \ 25 | --header 'accept: application/json' \ 26 | --header "authorization: Basic ${{ secrets.readme_api_key_base64 }}" \ 27 | | jq -r '.body') 28 | 29 | # Prepend new release notes to existing content 30 | new_content=$(echo -e "$formatted_notes\n\n$existing_content") 31 | payload=$(jq -n --arg nc "$new_content" '{"body": $nc}') 32 | 33 | # Update version history page with new release notes 34 | curl --request PUT \ 35 | --url https://dash.readme.com/api/v1/docs/mac-os-version-history \ 36 | --header 'accept: application/json' \ 37 | --header "authorization: Basic ${{ secrets.readme_api_key_base64 }}" \ 38 | --header 'content-type: application/json' \ 39 | --data "$payload" 40 | 41 | - name: Announce New Release in Slack 42 | uses: slackapi/slack-github-action@v1.24.0 43 | with: 44 | channel-id: "C063MQJMKJN" #sdk-releases 45 | payload: | 46 | { 47 | "text": "New Release: Branch MacOS SDK v${{ github.event.release.tag_name }}", 48 | "blocks": [ 49 | { 50 | "type": "header", 51 | "text": { 52 | "type": "plain_text", 53 | "text": ":rocket: New Release: Branch MacOS SDK v${{ github.event.release.tag_name }}", 54 | "emoji": true 55 | } 56 | }, 57 | { 58 | "type": "divider" 59 | }, 60 | { 61 | "type": "section", 62 | "text": { 63 | "type": "mrkdwn", 64 | "text": ":star: *What's New*" 65 | } 66 | }, 67 | { 68 | "type": "section", 69 | "text": { 70 | "type": "mrkdwn", 71 | "text": ${{ toJSON(github.event.release.body) }} 72 | } 73 | }, 74 | { 75 | "type": "divider" 76 | }, 77 | { 78 | "type": "actions", 79 | "elements": [ 80 | { 81 | "type": "button", 82 | "text": { 83 | "type": "plain_text", 84 | "text": ":git: GitHub Release", 85 | "emoji": true 86 | }, 87 | "value": "github", 88 | "action_id": "github", 89 | "url": "${{ github.event.release.html_url }}" 90 | } 91 | ] 92 | } 93 | ] 94 | } 95 | env: 96 | SLACK_BOT_TOKEN: ${{ secrets.SLACK_SDK_BOT_TOKEN }} 97 | -------------------------------------------------------------------------------- /Branch/BranchDelegate.h: -------------------------------------------------------------------------------- 1 | /** 2 | @file BranchDelegate.h 3 | @package Branch 4 | @brief Branch delegate protocol and notifications. 5 | 6 | @author Edward Smith 7 | @date June 30, 2017 8 | @copyright Copyright © 2017 Branch. All rights reserved. 9 | */ 10 | 11 | #import "BranchHeader.h" 12 | @class Branch, BranchSession; 13 | 14 | NS_ASSUME_NONNULL_BEGIN 15 | 16 | 17 | #ifndef BranchDelegate_h 18 | #define BranchDelegate_h 19 | 20 | /** 21 | @name Branch Notifications and delegate methods. 22 | 23 | The Branch SDK can signal your app in three different ways when an deep link is available for your app to 24 | handle: You can set a callback block, you can set a delegate, or you can add an observer for NSNotifications. 25 | You can choose the that best suits your app archetcture. 26 | */ 27 | 28 | #pragma mark BranchDelegate Protocol 29 | 30 | /** 31 | These delegate methods are called when while Branch is starting a new URL session and possibly opening a 32 | deep link. All the methods are optional. 33 | 34 | ```objc 35 | [Branch sharedInstance].delegate = delegateInstance; 36 | ``` 37 | */ 38 | @protocol BranchDelegate <NSObject> 39 | 40 | @optional 41 | - (void) branch:(Branch*)branch willStartSessionWithURL:(NSURL*_Nullable)url; 42 | - (void) branch:(Branch*)branch didStartSession:(BranchSession*)session; 43 | - (void) branch:(Branch*)branch failedToStartSessionWithURL:(NSURL*_Nullable)url 44 | error:(NSError*_Nullable)error; 45 | - (void) branch:(Branch*)branch didOpenURLWithSession:(BranchSession*)session; 46 | @end 47 | 48 | #pragma mark - Branch Notifications and Keys 49 | 50 | /** 51 | @name Branch Notifications 52 | 53 | The advantage of observing Branch NSNotifications is that it allows for more modularized code with greater 54 | separation of responsibility. Only those classes that care about Branch notifications need to know about 55 | them. This is particularly useful as your project grows larger and dependency management becomes an issue. 56 | 57 | #### **`BranchWillStartSessionNotification`** 58 | 59 | This notification is sent just before the Branch SDK is about to determine if there is a deep link for your 60 | app to handle. This usually involves a server call so it may take some time for the SDK to make the 61 | determination. 62 | 63 | ##### Notification Keys 64 | 65 | The notification `userInfo` dictionary may have these keys: 66 | 67 | Key | Value Type | Content 68 | :---:|:----------:|:------- 69 | `BranchURLKey` <br>(Optional) | NSURL | This is the URL if the Branch session was started with a URL. 70 | 71 | #### **`BranchDidStartSessionNotification`** 72 | 73 | This notification is sent when the Branch SDK has started a new URL session. There may or may not be a deep 74 | link for your app to handle. If there is, the `BranchSessionKey` value will have a BranchSession that 75 | contains the deep link content. 76 | 77 | If an error has occurred the `BranchErrorKey` value will contain an `NSError` that describes the error. 78 | 79 | ##### Notification Keys 80 | 81 | The notification `userInfo` dictionary may have these keys: 82 | 83 | Key | Value Type | Content 84 | :---:|:----------:|:------- 85 | `BranchURLKey`<br>(Optional) | NSURL | This is the URL that started the Branch session. 86 | `BranchSessionKey`<br>(Optional) | BranchSession | If the Branch session has a Branch deep link for your app to handle, this is the deep link content decoded into a BranchSession. 87 | `BranchErrorKey`<br>(Optional) | NSError | If an error occurred while starting the Branch session, this the NSError that describes the error. 88 | */ 89 | 90 | FOUNDATION_EXPORT NSString*const BranchWillStartSessionNotification; 91 | FOUNDATION_EXPORT NSString*const BranchDidStartSessionNotification; 92 | FOUNDATION_EXPORT NSString*const BranchDidOpenURLWithSessionNotification; 93 | 94 | FOUNDATION_EXPORT NSString*const BranchURLKey; 95 | FOUNDATION_EXPORT NSString*const BranchSessionKey; 96 | FOUNDATION_EXPORT NSString*const BranchErrorKey; 97 | 98 | #endif 99 | 100 | NS_ASSUME_NONNULL_END 101 | -------------------------------------------------------------------------------- /Branch/BNCUserAgentCollector.m: -------------------------------------------------------------------------------- 1 | // 2 | // BNCUserAgentCollector.m 3 | // Branch 4 | // 5 | // Created by Ernest Cho on 8/29/19. 6 | // Copyright © 2019 Branch, Inc. All rights reserved. 7 | // 8 | 9 | #import "BNCUserAgentCollector.h" 10 | #import "BNCDevice.h" 11 | @import WebKit; 12 | 13 | // expose a private method on BNCDevice 14 | @interface BNCDevice() 15 | + (NSString *)systemBuildVersion; 16 | @end 17 | 18 | @interface BNCUserAgentCollector() 19 | // need to hold onto the webview until the async user agent fetch is done 20 | @property (nonatomic, strong, readwrite) WKWebView *webview; 21 | @end 22 | 23 | @implementation BNCUserAgentCollector 24 | 25 | + (BNCUserAgentCollector *)instance { 26 | static BNCUserAgentCollector *collector; 27 | static dispatch_once_t onceToken; 28 | dispatch_once(&onceToken, ^{ 29 | collector = [BNCUserAgentCollector new]; 30 | }); 31 | return collector; 32 | } 33 | 34 | + (NSString *)userAgentKey { 35 | return @"BNC_USER_AGENT"; 36 | } 37 | 38 | + (NSString *)systemBuildVersionKey { 39 | return @"BNC_SYSTEM_BUILD_VERSION"; 40 | } 41 | 42 | - (void)loadUserAgentWithCompletion:(void (^)(NSString *userAgent))completion { 43 | [self loadUserAgentForSystemBuildVersion:[BNCDevice systemBuildVersion] withCompletion:completion]; 44 | } 45 | 46 | - (void)loadUserAgentForSystemBuildVersion:(NSString *)systemBuildVersion withCompletion:(void (^)(NSString *userAgent))completion { 47 | 48 | NSString *savedUserAgent = [self loadUserAgentForSystemBuildVersion:systemBuildVersion]; 49 | if (savedUserAgent) { 50 | self.userAgent = savedUserAgent; 51 | if (completion) { 52 | completion(savedUserAgent); 53 | } 54 | } else { 55 | [self collectUserAgentWithCompletion:^(NSString * _Nullable userAgent) { 56 | self.userAgent = userAgent; 57 | [self saveUserAgent:userAgent forSystemBuildVersion:systemBuildVersion]; 58 | if (completion) { 59 | completion(userAgent); 60 | } 61 | }]; 62 | } 63 | } 64 | 65 | // load user agent from preferences 66 | - (NSString *)loadUserAgentForSystemBuildVersion:(NSString *)systemBuildVersion { 67 | NSString *userAgent = nil; 68 | NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; 69 | NSString *savedUserAgent = (NSString *)[defaults valueForKey:[BNCUserAgentCollector userAgentKey]]; 70 | NSString *savedSystemBuildVersion = (NSString *)[defaults valueForKey:[BNCUserAgentCollector systemBuildVersionKey]]; 71 | 72 | if (savedUserAgent && [systemBuildVersion isEqualToString:savedSystemBuildVersion]) { 73 | userAgent = savedUserAgent; 74 | } 75 | return userAgent; 76 | } 77 | 78 | // save user agent to preferences 79 | - (void)saveUserAgent:(NSString *)userAgent forSystemBuildVersion:(NSString *)systemBuildVersion { 80 | if (userAgent && systemBuildVersion) { 81 | NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; 82 | [defaults setObject:userAgent forKey:[BNCUserAgentCollector userAgentKey]]; 83 | [defaults setObject:systemBuildVersion forKey:[BNCUserAgentCollector systemBuildVersionKey]]; 84 | } 85 | } 86 | 87 | // collect user agent from webkit. this is expensive. 88 | - (void)collectUserAgentWithCompletion:(void (^)(NSString *userAgent))completion { 89 | dispatch_async(dispatch_get_main_queue(), ^{ 90 | if (!self.webview) { 91 | self.webview = [[WKWebView alloc] initWithFrame:CGRectZero]; 92 | } 93 | 94 | [self.webview evaluateJavaScript:@"navigator.userAgent;" completionHandler:^(id _Nullable response, NSError * _Nullable error) { 95 | if (completion) { 96 | if (response) { 97 | completion(response); 98 | 99 | // release the webview 100 | self.webview = nil; 101 | } else { 102 | // retry if we failed to obtain user agent. This occurs on iOS simulators. 103 | [self collectUserAgentWithCompletion:completion]; 104 | } 105 | } 106 | }]; 107 | }); 108 | } 109 | 110 | @end 111 | -------------------------------------------------------------------------------- /Examples/TestDeepLinking/TestDeepLinking/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // TestDeepLinking 4 | // 5 | // Created by Nidhi on 2/3/21. 6 | // 7 | 8 | #import "AppDelegate.h" 9 | #import <Branch/Branch.h> 10 | #import <Branch/BNCLog.h> 11 | #import <Branch/BNCThreads.h> 12 | 13 | static AppDelegate* appDelegate = nil; 14 | static BNCLogOutputFunctionPtr originalLogHook = NULL; 15 | 16 | void APPLogHookFunction(NSDate*_Nonnull timestamp, BNCLogLevel level, NSString*_Nullable message); 17 | void APPLogHookFunction(NSDate*_Nonnull timestamp, BNCLogLevel level, NSString*_Nullable message) { 18 | [appDelegate processLogMessage:message]; 19 | if (originalLogHook) { 20 | originalLogHook(timestamp, level, message); 21 | } 22 | } 23 | 24 | @interface AppDelegate () 25 | 26 | @property (strong) IBOutlet NSWindow *window; 27 | @end 28 | 29 | @implementation AppDelegate 30 | 31 | - (void)applicationDidFinishLaunching:(NSNotification *)aNotification { 32 | 33 | appDelegate = self; 34 | originalLogHook = BNCLogOutputFunction(); 35 | BNCLogSetOutputFunction(APPLogHookFunction); 36 | BNCLogSetDisplayLevel(BNCLogLevelAll); 37 | 38 | [[NSNotificationCenter defaultCenter] 39 | addObserver:self 40 | selector:@selector(branchWillStartSession:) 41 | name:BranchWillStartSessionNotification 42 | object:nil]; 43 | 44 | [[NSNotificationCenter defaultCenter] 45 | addObserver:self 46 | selector:@selector(branchDidStartSession:) 47 | name:BranchDidStartSessionNotification 48 | object:nil]; 49 | 50 | [[NSNotificationCenter defaultCenter] 51 | addObserver:self 52 | selector:@selector(branchOpenedURLNotification:) 53 | name:BranchDidOpenURLWithSessionNotification 54 | object:nil]; 55 | 56 | BranchConfiguration *configuration = [[BranchConfiguration alloc] initWithKey:@"key_live_jcZkwmLUm17zGqCXKyh6QjdiAyjDodHI"]; 57 | 58 | configuration.branchAPIServiceURL = @"https://api.branch.io"; 59 | configuration.key = @"key_live_jcZkwmLUm17zGqCXKyh6QjdiAyjDodHI"; 60 | 61 | [[Branch sharedInstance] startWithConfiguration:configuration]; 62 | } 63 | 64 | - (BOOL) string:(NSString*)string matchesRegex:(NSString*)regex { 65 | NSError *error = NULL; 66 | NSRegularExpression *ns_regex = 67 | [NSRegularExpression regularExpressionWithPattern:regex options:0 error:&error]; 68 | NSRange range = [ns_regex rangeOfFirstMatchInString:string options:0 range:NSMakeRange(0, string.length)]; 69 | return (range.location == NSNotFound) ? NO : YES; 70 | } 71 | 72 | 73 | - (void) processLogMessage:(NSString *)message { 74 | if (([self string:message matchesRegex: 75 | @"^\\[branch\\.io\\] BNCNetworkService\\.m\\([0-9]+\\) Debug: Network start"])&& 76 | ([message containsString:@"https://cdn.branch.io/sdk/uriskiplist_v0.json"] == NO)) { 77 | BNCPerformBlockOnMainThreadAsync(^{ 78 | 79 | NSLog(@"---------------\n%@\n--------------", message); 80 | // self.viewController.requestTextView.string = message; 81 | }); 82 | } else 83 | if (([self string:message matchesRegex: 84 | @"^\\[branch\\.io\\] BNCNetworkService\\.m\\([0-9]+\\) Debug: Network finish"])&& 85 | ([message containsString:@"https://cdn.branch.io/sdk/uriskiplist_v0.json"] == NO)) { 86 | BNCPerformBlockOnMainThreadAsync(^{ 87 | //self.viewController.responseTextView.string = message; 88 | }); 89 | } 90 | } 91 | 92 | 93 | #pragma mark - Branch Notifications 94 | 95 | - (void) branchWillStartSession:(NSNotification*)notification { 96 | self.notification.stringValue = notification.name; 97 | } 98 | 99 | - (void) branchDidStartSession:(NSNotification*)notification { 100 | 101 | BranchSession *session = notification.userInfo[BranchSessionKey]; 102 | 103 | NSString *data = (session && session.data) ? session.data.description : @""; 104 | self.sessionData.string = data ; 105 | 106 | } 107 | 108 | - (void) branchOpenedURLNotification:(NSNotification*)notification { 109 | self.notification.stringValue = notification.name; 110 | } 111 | 112 | 113 | - (void)applicationWillTerminate:(NSNotification *)aNotification { 114 | // Insert code here to tear down your application 115 | } 116 | 117 | 118 | @end 119 | -------------------------------------------------------------------------------- /BranchTests/BNCKeyChain.Test.m: -------------------------------------------------------------------------------- 1 | /** 2 | @file BNCKeyChain.Test.m 3 | @package BranchTests 4 | @brief BNCKeyChain tests. 5 | 6 | @author Edward Smith 7 | @date January 8, 2018 8 | @copyright Copyright © 2018 Branch. All rights reserved. 9 | */ 10 | 11 | #import "BNCTestCase.h" 12 | #import "BNCKeyChain.h" 13 | #import "BNCDevice.h" 14 | #import "BNCApplication.h" 15 | 16 | @interface BNCKeyChainTest : BNCTestCase 17 | @end 18 | 19 | @implementation BNCKeyChainTest 20 | 21 | - (void)testKeyChain { 22 | NSError *error = nil; 23 | NSString*value = nil; 24 | NSArray *array, *array1; 25 | NSString*const kServiceName = @"Service"; 26 | NSString*const kServiceName2 = @"Service2"; 27 | double systemVersion = [BNCDevice currentDevice].systemVersion.doubleValue; 28 | NSString*systemName = [BNCDevice currentDevice].systemName; 29 | 30 | // Find a signed bundle: 31 | 32 | NSString*teamID = [BNCApplication currentApplication].teamID; 33 | NSString*bundleID = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleIdentifier"]; 34 | 35 | if (teamID.length == 0 || bundleID.length == 0) { 36 | // The app bundle needs to be signed probably. 37 | //XCTAssertTrue(bundleID.length > 0 && teamID.length > 0); 38 | return; 39 | } 40 | NSString*securityGroup = [NSString stringWithFormat:@"%@.%@", teamID, bundleID]; 41 | BNCKeyChain*keychain = [[BNCKeyChain alloc] initWithSecurityAccessGroup:securityGroup]; 42 | 43 | // Remove and validate gone: 44 | 45 | error = [keychain removeValuesForService:kServiceName key:nil]; 46 | if (![systemName isEqualToString:@"mac_OS"] && systemVersion >= 10.0 && systemVersion < 11.0) 47 | { XCTAssertTrue(error == nil || error.code == -34018); } 48 | else 49 | { XCTAssertTrue(error == nil); } 50 | 51 | array = [keychain retrieveKeysWithService:kServiceName error:&error]; 52 | XCTAssertTrue(array.count == 0 && error == errSecSuccess); 53 | 54 | // Check some keys: 55 | 56 | value = [keychain retrieveValueForService:kServiceName key:@"key1" error:&error]; 57 | XCTAssertTrue(value == nil && error.code == errSecItemNotFound); 58 | 59 | value = [keychain retrieveValueForService:kServiceName key:@"key2" error:&error]; 60 | XCTAssertTrue(value == nil && error.code == errSecItemNotFound); 61 | 62 | // Test that local storage works: 63 | 64 | error = [keychain storeValue:@"1xyz123" forService:kServiceName key:@"key1"]; 65 | XCTAssertTrue(error == nil); 66 | value = [keychain retrieveValueForService:kServiceName key:@"key1" error:&error]; 67 | XCTAssertTrue(error == nil && [value isEqualToString:@"1xyz123"]); 68 | 69 | error = [keychain storeValue:@"2xyz123" forService:kServiceName key:@"key2"]; 70 | XCTAssertTrue(error == nil); 71 | value = [keychain retrieveValueForService:kServiceName key:@"key2" error:&error]; 72 | XCTAssertTrue(error == nil && [value isEqualToString:@"2xyz123"]); 73 | 74 | error = [keychain storeValue:@"3xyz123" forService:kServiceName2 key:@"key3"]; 75 | XCTAssertTrue(error == nil); 76 | value = [keychain retrieveValueForService:kServiceName2 key:@"key3" error:&error]; 77 | XCTAssertTrue(error == nil && [value isEqualToString:@"3xyz123"]); 78 | 79 | // Remove by service: 80 | 81 | error = [keychain removeValuesForService:kServiceName key:nil]; 82 | value = [keychain retrieveValueForService:kServiceName key:@"key1" error:&error]; 83 | XCTAssertTrue(value == nil && error.code == errSecItemNotFound); 84 | 85 | value = [keychain retrieveValueForService:kServiceName key:@"key2" error:&error]; 86 | XCTAssertTrue(value == nil && error.code == errSecItemNotFound); 87 | 88 | value = [keychain retrieveValueForService:kServiceName2 key:@"key3" error:&error]; 89 | XCTAssertTrue(error == nil && [value isEqualToString:@"3xyz123"]); 90 | 91 | // Check service2 values: 92 | 93 | error = [keychain storeValue:@"4xyz123" forService:kServiceName2 key:@"key4"]; 94 | XCTAssertTrue(error == nil); 95 | array1 = [keychain retrieveKeysWithService:kServiceName2 error:&error]; 96 | XCTAssertNil(error); 97 | NSSet*s1 = [NSSet setWithArray:array1]; 98 | NSSet*s2 = [NSSet setWithArray:@[ @"key3", @"key4" ]]; 99 | XCTAssertEqualObjects(s1, s2); 100 | } 101 | 102 | @end 103 | -------------------------------------------------------------------------------- /BranchTests/BNCUserAgentCollectorTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // BNCUserAgentCollectorTests.m 3 | // Branch-SDK-Tests 4 | // 5 | // Created by Ernest Cho on 8/29/19. 6 | // Copyright © 2019 Branch, Inc. All rights reserved. 7 | // 8 | #import <XCTest/XCTest.h> 9 | #import "BNCUserAgentCollector.h" 10 | 11 | // expose private methods for unit testing 12 | @interface BNCUserAgentCollector() 13 | 14 | + (NSString *)userAgentKey; 15 | + (NSString *)systemBuildVersionKey; 16 | 17 | - (NSString *)loadUserAgentForSystemBuildVersion:(NSString *)systemBuildVersion; 18 | - (void)saveUserAgent:(NSString *)userAgent forSystemBuildVersion:(NSString *)systemBuildVersion; 19 | 20 | - (void)loadUserAgentForSystemBuildVersion:(NSString *)systemBuildVersion withCompletion:(void (^)(NSString *userAgent))completion; 21 | 22 | - (void)collectUserAgentWithCompletion:(void (^)(NSString *userAgent))completion; 23 | 24 | @end 25 | 26 | @interface BNCUserAgentCollectorTests : XCTestCase 27 | 28 | @end 29 | 30 | @implementation BNCUserAgentCollectorTests 31 | 32 | + (void)setUp { 33 | [BNCUserAgentCollectorTests resetPersistentData]; 34 | } 35 | 36 | - (void)setUp { 37 | 38 | } 39 | 40 | - (void)tearDown { 41 | [BNCUserAgentCollectorTests resetPersistentData]; 42 | } 43 | 44 | + (void)resetPersistentData { 45 | NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; 46 | [defaults setObject:nil forKey:[BNCUserAgentCollector userAgentKey]]; 47 | [defaults setObject:nil forKey:[BNCUserAgentCollector systemBuildVersionKey]]; 48 | } 49 | 50 | - (void)testResetPersistentData { 51 | NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; 52 | NSString *savedUserAgent = (NSString *)[defaults valueForKey:[BNCUserAgentCollector userAgentKey]]; 53 | NSString *savedSystemBuildVersion = (NSString *)[defaults valueForKey:[BNCUserAgentCollector systemBuildVersionKey]]; 54 | 55 | XCTAssertNil(savedUserAgent); 56 | XCTAssertNil(savedSystemBuildVersion); 57 | } 58 | 59 | - (void)testSaveAndLoadUserAgent { 60 | NSString *systemBuildVersion = @"test"; 61 | NSString *userAgent = @"UserAgent"; 62 | 63 | BNCUserAgentCollector *collector = [BNCUserAgentCollector new]; 64 | [collector saveUserAgent:userAgent forSystemBuildVersion:systemBuildVersion]; 65 | NSString *expected = [collector loadUserAgentForSystemBuildVersion:systemBuildVersion]; 66 | XCTAssertTrue([userAgent isEqualToString:expected]); 67 | } 68 | 69 | - (void)testCollectUserAgent { 70 | XCTestExpectation *expectation = [self expectationWithDescription:@"expectation"]; 71 | 72 | BNCUserAgentCollector *collector = [BNCUserAgentCollector new]; 73 | [collector collectUserAgentWithCompletion:^(NSString * _Nullable userAgent) { 74 | XCTAssertNotNil(userAgent); 75 | XCTAssertTrue([userAgent containsString:@"AppleWebKit"]); 76 | [expectation fulfill]; 77 | }]; 78 | 79 | [self waitForExpectationsWithTimeout:2.0 handler:^(NSError * _Nullable error) { 80 | 81 | }]; 82 | } 83 | 84 | - (void)testLoadUserAgent_EmptyDataStore { 85 | XCTestExpectation *expectation = [self expectationWithDescription:@"expectation"]; 86 | NSString *systemBuildVersion = @"test"; 87 | 88 | BNCUserAgentCollector *collector = [BNCUserAgentCollector new]; 89 | [collector loadUserAgentForSystemBuildVersion:systemBuildVersion withCompletion:^(NSString * _Nullable userAgent) { 90 | XCTAssertNotNil(userAgent); 91 | XCTAssertTrue([userAgent containsString:@"AppleWebKit"]); 92 | [expectation fulfill]; 93 | }]; 94 | 95 | [self waitForExpectationsWithTimeout:2.0 handler:^(NSError * _Nullable error) { 96 | 97 | }]; 98 | } 99 | 100 | - (void)testLoadUserAgent_FilledDataStore { 101 | XCTestExpectation *expectation = [self expectationWithDescription:@"expectation"]; 102 | NSString *systemBuildVersion = @"test"; 103 | NSString *savedUserAgent = @"UserAgent"; 104 | 105 | BNCUserAgentCollector *collector = [BNCUserAgentCollector new]; 106 | [collector saveUserAgent:savedUserAgent forSystemBuildVersion:systemBuildVersion]; 107 | [collector loadUserAgentForSystemBuildVersion:systemBuildVersion withCompletion:^(NSString * _Nullable userAgent) { 108 | XCTAssertNotNil(userAgent); 109 | XCTAssertTrue([userAgent isEqualToString:savedUserAgent]); 110 | XCTAssertFalse([userAgent containsString:@"AppleWebKit"]); 111 | [expectation fulfill]; 112 | }]; 113 | 114 | [self waitForExpectationsWithTimeout:2.0 handler:^(NSError * _Nullable error) { 115 | 116 | }]; 117 | } 118 | 119 | @end 120 | -------------------------------------------------------------------------------- /Branch/BranchError.m: -------------------------------------------------------------------------------- 1 | /** 2 | @file BranchError.m 3 | @package Branch 4 | @brief Branch errors. 5 | 6 | @author Qinwei Gong 7 | @date November 2014 8 | @copyright Copyright © 2014 Branch. All rights reserved. 9 | */ 10 | 11 | #import "BranchError.h" 12 | #import "BNCLocalization.h" 13 | 14 | NSString * const BNCErrorDomain = @"io.branch.sdk.error"; 15 | 16 | __attribute__((constructor)) void BNCForceNSErrorCategoryToLoad() { 17 | // Nothing here, but forces linker to load the category. 18 | } 19 | 20 | @implementation NSError (Branch) 21 | 22 | + (NSString*) messageForCode:(BNCErrorCode)code { 23 | 24 | // The order is important! 25 | 26 | static NSString* const messages[] = { 27 | 28 | // BNCInitError 29 | @"The Branch user session has not been initialized.", 30 | 31 | // BNCDuplicateResourceError 32 | @"A resource with this identifier already exists.", 33 | 34 | // BNCRedeemCreditsError 35 | @"You're trying to redeem more credits than are available. Have you loaded rewards?", 36 | 37 | // BNCBadRequestError 38 | @"The network request was invalid.", 39 | 40 | // BNCServerProblemError 41 | @"Trouble reaching the Branch servers, please try again shortly.", 42 | 43 | // BNCNilLogError 44 | @"Can't log error messages because the logger is set to nil.", 45 | 46 | // BNCVersionError 47 | @"Incompatible version.", 48 | 49 | // BNCNetworkServiceInterfaceError 50 | @"The underlying network service does not conform to the BNCNetworkOperationProtocol.", 51 | 52 | // BNCContentIdentifierError 53 | @"A canonical identifier or title are required to uniquely identify content.", 54 | 55 | // BNCSpotlightNotAvailableError 56 | @"The Core Spotlight indexing service is not available on this device.", 57 | 58 | // BNCSpotlightTitleError 59 | @"Spotlight indexing requires a title.", 60 | 61 | // BNCRedeemZeroCreditsError 62 | @"Can't redeem zero credits.", 63 | 64 | // BNCSpotlightIdentifierError 65 | @"The Spotlight identifier is required to remove indexing from spotlight.", 66 | 67 | //BNCSpotlightPublicIndexError 68 | @"Spotlight cannot remove publicly indexed content.", 69 | 70 | //BNCTrackingDisabledError 71 | @"User tracking is disabled." 72 | }; 73 | 74 | #define _countof(array) (sizeof(array)/sizeof(array[0])) 75 | 76 | // Sanity check 77 | #pragma clang diagnostic push 78 | #pragma clang diagnostic ignored "-Wunreachable-code" 79 | if (_countof(messages) != (BNCHighestError - BNCInitError)) { 80 | [NSException raise:NSInternalInconsistencyException format:@"Branch error message count is wrong."]; 81 | return @"Branch encountered an error."; 82 | } 83 | #pragma clang diagnostic pop 84 | 85 | if (code < BNCInitError || code >= BNCHighestError) 86 | return @"Branch encountered an error."; 87 | 88 | return messages[code - BNCInitError]; 89 | } 90 | 91 | + (NSError*_Nonnull) branchErrorWithCode:(BNCErrorCode)errorCode 92 | error:(NSError*)error 93 | localizedMessage:(NSString*_Nullable)message { 94 | 95 | NSMutableDictionary *userInfo = [NSMutableDictionary new]; 96 | 97 | NSString *localizedString = BNCLocalizedString([self messageForCode:errorCode]); 98 | if (localizedString) userInfo[NSLocalizedDescriptionKey] = localizedString; 99 | if (message) { 100 | userInfo[NSLocalizedFailureReasonErrorKey] = message; 101 | } 102 | if (error) { 103 | userInfo[NSUnderlyingErrorKey] = error; 104 | if (!userInfo[NSLocalizedFailureReasonErrorKey] && error.localizedDescription) 105 | userInfo[NSLocalizedFailureReasonErrorKey] = error.localizedDescription; 106 | } 107 | 108 | return [NSError errorWithDomain:BNCErrorDomain code:errorCode userInfo:userInfo]; 109 | } 110 | 111 | + (NSError*_Nonnull) branchErrorWithCode:(BNCErrorCode)errorCode { 112 | return [NSError branchErrorWithCode:errorCode error:nil localizedMessage:nil]; 113 | } 114 | 115 | + (NSError*_Nonnull) branchErrorWithCode:(BNCErrorCode)errorCode error:(NSError*_Nullable)error { 116 | return [NSError branchErrorWithCode:errorCode error:error localizedMessage:nil]; 117 | } 118 | 119 | + (NSError*_Nonnull) branchErrorWithCode:(BNCErrorCode)errorCode localizedMessage:(NSString*_Nullable)message { 120 | return [NSError branchErrorWithCode:errorCode error:nil localizedMessage:message]; 121 | } 122 | 123 | @end 124 | --------------------------------------------------------------------------------