├── .coveralls.yml ├── spec_helper.rb ├── libPhoneNumber-Demo ├── libPhoneNumber-Demo │ ├── Assets.xcassets │ │ ├── Contents.json │ │ └── AppIcon.appiconset │ │ │ ├── 1024.png │ │ │ └── Contents.json │ ├── Preview Content │ │ └── Preview Assets.xcassets │ │ │ └── Contents.json │ ├── libPhoneNumber-iOS.png │ ├── GeocodingView.swift │ ├── MenuView.swift │ ├── FormatterView.swift │ ├── AppDelegate.swift │ ├── Info.plist │ ├── GeocodingSearchView.swift │ ├── Locales.swift │ ├── SceneDelegate.swift │ ├── Base.lproj │ │ └── LaunchScreen.storyboard │ ├── ShortNumberUtilView.swift │ ├── PhoneUtilView.swift │ └── GeocodingTableView.swift ├── libPhoneNumber-Demo-SPM.xcodeproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ └── xcshareddata │ │ └── xcschemes │ │ └── libPhoneNumber-Demo.xcscheme ├── libPhoneNumber-Demo.xcodeproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ └── xcshareddata │ │ └── xcschemes │ │ └── libPhoneNumber-Demo.xcscheme ├── libPhoneNumber-Demo.xcworkspace │ ├── xcshareddata │ │ └── IDEWorkspaceChecks.plist │ └── contents.xcworkspacedata ├── Podfile └── README.md ├── libPhoneNumberTestsCommon ├── NBTestingMetaData.m ├── libPhoneNumberMetaDataForTesting.zip ├── NBTestingMetaData.h ├── NSBundle+Extensions.h └── NSBundle+Extensions.m ├── libPhoneNumberGeocodingTests ├── TestingSource.bundle │ ├── de.db │ ├── en.db │ ├── it.db │ └── ko.db ├── libPhoneNumberGeocodingTests.xctestplan └── Info.plist ├── libPhoneNumberGeocodingMetaData ├── GeocodingMetaData.bundle │ ├── ar.db │ ├── be.db │ ├── bg.db │ ├── bs.db │ ├── de.db │ ├── el.db │ ├── en.db │ ├── es.db │ ├── fa.db │ ├── fi.db │ ├── fr.db │ ├── hr.db │ ├── hu.db │ ├── hy.db │ ├── id.db │ ├── it.db │ ├── iw.db │ ├── ja.db │ ├── ko.db │ ├── nl.db │ ├── pl.db │ ├── pt.db │ ├── ro.db │ ├── ru.db │ ├── sq.db │ ├── sr.db │ ├── sv.db │ ├── th.db │ ├── tr.db │ ├── uk.db │ ├── vi.db │ ├── zh.db │ └── zh_Hant.db ├── _HeaderOnlyShim.c └── _EmptyHeader.h ├── libPhoneNumberInternal ├── _HeaderOnlyShim.c ├── NBGeneratedPhoneNumberMetaData.h ├── NBRegExMatcher.h └── NBRegularExpressionCache.h ├── libPhoneNumberShortNumberInternal ├── _HeaderOnlyShim.c └── NBGeneratedShortNumberMetaData.h ├── libPhoneNumber.xcodeproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── WorkspaceSettings.xcsettings └── xcshareddata │ └── xcschemes │ ├── libPhoneNumber.xcscheme │ ├── libPhoneNumberGeocoding.xcscheme │ └── libPhoneNumberShortNumber.xcscheme ├── libPhoneNumber-GeocodingParser ├── Podfile ├── libPhoneNumber-GeocodingParser.xcodeproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ └── xcshareddata │ │ └── xcschemes │ │ └── libPhoneNumber-GeocodingParser.xcscheme ├── libPhoneNumber-GeocodingParser.xcworkspace │ ├── xcshareddata │ │ └── IDEWorkspaceChecks.plist │ └── contents.xcworkspacedata ├── libPhoneNumber-GeocodingParser │ ├── NBGeocoderMetadataParser.h │ ├── NBSQLiteDatabaseConnection.h │ ├── NSString+Extensions.h │ ├── ArgumentParser.h │ ├── NSString+Extensions.m │ ├── ArgumentParser.m │ ├── NBGeocoderMetadataParser.m │ ├── NBSQLiteDatabaseConnection.m │ └── main.m └── README.md ├── libPhoneNumber ├── NBPhoneNumberDefines.m ├── PrivacyInfo.xcprivacy ├── NSArray+NBAdditions.h ├── NBPhoneNumberDesc.h ├── NBMetadataHelper.h ├── NBAsYouTypeFormatter.h ├── Info.plist ├── NBPhoneNumber.h ├── libPhoneNumber.h ├── NSArray+NBAdditions.m ├── NBNumberFormat.h ├── NBRegularExpressionCache.m ├── NBPhoneMetaData.h ├── NBRegExMatcher.m ├── NBPhoneNumberDefines.h ├── NBPhoneNumberDesc.m ├── NBNumberFormat.m └── NBPhoneNumber.m ├── .slather.yml ├── .gitignore ├── libPhoneNumberTests ├── libPhoneNumberTests.xctestplan ├── Info.plist └── NBPhoneNumberParsingPerfTest.m ├── libPhoneNumberShortNumberTests ├── libPhoneNumberShortNumberTests.xctestplan ├── NBShortNumberTestHelper.h ├── Info.plist └── NBShortNumberTestHelper.m ├── libPhoneNumberGeocoding ├── libPhoneNumberGeocoding.h ├── Info.plist ├── NBGeocoderMetaDataHelper.h ├── README.md ├── NBGeocoderMetaDataHelper.m └── NBPhoneNumberOfflineGeocoder.m ├── libPhoneNumberShortNumber ├── libPhoneNumberShortNumber.h ├── Info.plist ├── NBShortNumberMetadataHelper.h ├── README.md └── NBShortNumberMetadataHelper.m ├── .travis.yml ├── libPhoneNumberShortNumber.podspec ├── libPhoneNumberGeocoding.podspec ├── AUTHORS ├── libPhoneNumber-iOS.podspec ├── Package.swift └── scripts ├── GeneratePhoneNumberMetaDataFiles.sh ├── updateProjectVersions.swift └── versionCommitter.swift /.coveralls.yml: -------------------------------------------------------------------------------- 1 | service_name: travis-ci 2 | -------------------------------------------------------------------------------- /spec_helper.rb: -------------------------------------------------------------------------------- 1 | require 'coveralls' 2 | Coveralls.wear! 3 | -------------------------------------------------------------------------------- /libPhoneNumber-Demo/libPhoneNumber-Demo/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /libPhoneNumberTestsCommon/NBTestingMetaData.m: -------------------------------------------------------------------------------- 1 | #include "NBTestingMetaData.h" 2 | 3 | z_const size_t kPhoneNumberMetaDataForTestingExpandedLength = 34035; 4 | -------------------------------------------------------------------------------- /libPhoneNumberGeocodingTests/TestingSource.bundle/de.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iziz/libPhoneNumber-iOS/HEAD/libPhoneNumberGeocodingTests/TestingSource.bundle/de.db -------------------------------------------------------------------------------- /libPhoneNumberGeocodingTests/TestingSource.bundle/en.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iziz/libPhoneNumber-iOS/HEAD/libPhoneNumberGeocodingTests/TestingSource.bundle/en.db -------------------------------------------------------------------------------- /libPhoneNumberGeocodingTests/TestingSource.bundle/it.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iziz/libPhoneNumber-iOS/HEAD/libPhoneNumberGeocodingTests/TestingSource.bundle/it.db -------------------------------------------------------------------------------- /libPhoneNumberGeocodingTests/TestingSource.bundle/ko.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iziz/libPhoneNumber-iOS/HEAD/libPhoneNumberGeocodingTests/TestingSource.bundle/ko.db -------------------------------------------------------------------------------- /libPhoneNumber-Demo/libPhoneNumber-Demo/Preview Content/Preview Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /libPhoneNumber-Demo/libPhoneNumber-Demo/libPhoneNumber-iOS.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iziz/libPhoneNumber-iOS/HEAD/libPhoneNumber-Demo/libPhoneNumber-Demo/libPhoneNumber-iOS.png -------------------------------------------------------------------------------- /libPhoneNumberGeocodingMetaData/GeocodingMetaData.bundle/ar.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iziz/libPhoneNumber-iOS/HEAD/libPhoneNumberGeocodingMetaData/GeocodingMetaData.bundle/ar.db -------------------------------------------------------------------------------- /libPhoneNumberGeocodingMetaData/GeocodingMetaData.bundle/be.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iziz/libPhoneNumber-iOS/HEAD/libPhoneNumberGeocodingMetaData/GeocodingMetaData.bundle/be.db -------------------------------------------------------------------------------- /libPhoneNumberGeocodingMetaData/GeocodingMetaData.bundle/bg.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iziz/libPhoneNumber-iOS/HEAD/libPhoneNumberGeocodingMetaData/GeocodingMetaData.bundle/bg.db -------------------------------------------------------------------------------- /libPhoneNumberGeocodingMetaData/GeocodingMetaData.bundle/bs.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iziz/libPhoneNumber-iOS/HEAD/libPhoneNumberGeocodingMetaData/GeocodingMetaData.bundle/bs.db -------------------------------------------------------------------------------- /libPhoneNumberGeocodingMetaData/GeocodingMetaData.bundle/de.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iziz/libPhoneNumber-iOS/HEAD/libPhoneNumberGeocodingMetaData/GeocodingMetaData.bundle/de.db -------------------------------------------------------------------------------- /libPhoneNumberGeocodingMetaData/GeocodingMetaData.bundle/el.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iziz/libPhoneNumber-iOS/HEAD/libPhoneNumberGeocodingMetaData/GeocodingMetaData.bundle/el.db -------------------------------------------------------------------------------- /libPhoneNumberGeocodingMetaData/GeocodingMetaData.bundle/en.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iziz/libPhoneNumber-iOS/HEAD/libPhoneNumberGeocodingMetaData/GeocodingMetaData.bundle/en.db -------------------------------------------------------------------------------- /libPhoneNumberGeocodingMetaData/GeocodingMetaData.bundle/es.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iziz/libPhoneNumber-iOS/HEAD/libPhoneNumberGeocodingMetaData/GeocodingMetaData.bundle/es.db -------------------------------------------------------------------------------- /libPhoneNumberGeocodingMetaData/GeocodingMetaData.bundle/fa.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iziz/libPhoneNumber-iOS/HEAD/libPhoneNumberGeocodingMetaData/GeocodingMetaData.bundle/fa.db -------------------------------------------------------------------------------- /libPhoneNumberGeocodingMetaData/GeocodingMetaData.bundle/fi.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iziz/libPhoneNumber-iOS/HEAD/libPhoneNumberGeocodingMetaData/GeocodingMetaData.bundle/fi.db -------------------------------------------------------------------------------- /libPhoneNumberGeocodingMetaData/GeocodingMetaData.bundle/fr.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iziz/libPhoneNumber-iOS/HEAD/libPhoneNumberGeocodingMetaData/GeocodingMetaData.bundle/fr.db -------------------------------------------------------------------------------- /libPhoneNumberGeocodingMetaData/GeocodingMetaData.bundle/hr.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iziz/libPhoneNumber-iOS/HEAD/libPhoneNumberGeocodingMetaData/GeocodingMetaData.bundle/hr.db -------------------------------------------------------------------------------- /libPhoneNumberGeocodingMetaData/GeocodingMetaData.bundle/hu.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iziz/libPhoneNumber-iOS/HEAD/libPhoneNumberGeocodingMetaData/GeocodingMetaData.bundle/hu.db -------------------------------------------------------------------------------- /libPhoneNumberGeocodingMetaData/GeocodingMetaData.bundle/hy.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iziz/libPhoneNumber-iOS/HEAD/libPhoneNumberGeocodingMetaData/GeocodingMetaData.bundle/hy.db -------------------------------------------------------------------------------- /libPhoneNumberGeocodingMetaData/GeocodingMetaData.bundle/id.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iziz/libPhoneNumber-iOS/HEAD/libPhoneNumberGeocodingMetaData/GeocodingMetaData.bundle/id.db -------------------------------------------------------------------------------- /libPhoneNumberGeocodingMetaData/GeocodingMetaData.bundle/it.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iziz/libPhoneNumber-iOS/HEAD/libPhoneNumberGeocodingMetaData/GeocodingMetaData.bundle/it.db -------------------------------------------------------------------------------- /libPhoneNumberGeocodingMetaData/GeocodingMetaData.bundle/iw.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iziz/libPhoneNumber-iOS/HEAD/libPhoneNumberGeocodingMetaData/GeocodingMetaData.bundle/iw.db -------------------------------------------------------------------------------- /libPhoneNumberGeocodingMetaData/GeocodingMetaData.bundle/ja.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iziz/libPhoneNumber-iOS/HEAD/libPhoneNumberGeocodingMetaData/GeocodingMetaData.bundle/ja.db -------------------------------------------------------------------------------- /libPhoneNumberGeocodingMetaData/GeocodingMetaData.bundle/ko.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iziz/libPhoneNumber-iOS/HEAD/libPhoneNumberGeocodingMetaData/GeocodingMetaData.bundle/ko.db -------------------------------------------------------------------------------- /libPhoneNumberGeocodingMetaData/GeocodingMetaData.bundle/nl.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iziz/libPhoneNumber-iOS/HEAD/libPhoneNumberGeocodingMetaData/GeocodingMetaData.bundle/nl.db -------------------------------------------------------------------------------- /libPhoneNumberGeocodingMetaData/GeocodingMetaData.bundle/pl.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iziz/libPhoneNumber-iOS/HEAD/libPhoneNumberGeocodingMetaData/GeocodingMetaData.bundle/pl.db -------------------------------------------------------------------------------- /libPhoneNumberGeocodingMetaData/GeocodingMetaData.bundle/pt.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iziz/libPhoneNumber-iOS/HEAD/libPhoneNumberGeocodingMetaData/GeocodingMetaData.bundle/pt.db -------------------------------------------------------------------------------- /libPhoneNumberGeocodingMetaData/GeocodingMetaData.bundle/ro.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iziz/libPhoneNumber-iOS/HEAD/libPhoneNumberGeocodingMetaData/GeocodingMetaData.bundle/ro.db -------------------------------------------------------------------------------- /libPhoneNumberGeocodingMetaData/GeocodingMetaData.bundle/ru.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iziz/libPhoneNumber-iOS/HEAD/libPhoneNumberGeocodingMetaData/GeocodingMetaData.bundle/ru.db -------------------------------------------------------------------------------- /libPhoneNumberGeocodingMetaData/GeocodingMetaData.bundle/sq.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iziz/libPhoneNumber-iOS/HEAD/libPhoneNumberGeocodingMetaData/GeocodingMetaData.bundle/sq.db -------------------------------------------------------------------------------- /libPhoneNumberGeocodingMetaData/GeocodingMetaData.bundle/sr.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iziz/libPhoneNumber-iOS/HEAD/libPhoneNumberGeocodingMetaData/GeocodingMetaData.bundle/sr.db -------------------------------------------------------------------------------- /libPhoneNumberGeocodingMetaData/GeocodingMetaData.bundle/sv.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iziz/libPhoneNumber-iOS/HEAD/libPhoneNumberGeocodingMetaData/GeocodingMetaData.bundle/sv.db -------------------------------------------------------------------------------- /libPhoneNumberGeocodingMetaData/GeocodingMetaData.bundle/th.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iziz/libPhoneNumber-iOS/HEAD/libPhoneNumberGeocodingMetaData/GeocodingMetaData.bundle/th.db -------------------------------------------------------------------------------- /libPhoneNumberGeocodingMetaData/GeocodingMetaData.bundle/tr.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iziz/libPhoneNumber-iOS/HEAD/libPhoneNumberGeocodingMetaData/GeocodingMetaData.bundle/tr.db -------------------------------------------------------------------------------- /libPhoneNumberGeocodingMetaData/GeocodingMetaData.bundle/uk.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iziz/libPhoneNumber-iOS/HEAD/libPhoneNumberGeocodingMetaData/GeocodingMetaData.bundle/uk.db -------------------------------------------------------------------------------- /libPhoneNumberGeocodingMetaData/GeocodingMetaData.bundle/vi.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iziz/libPhoneNumber-iOS/HEAD/libPhoneNumberGeocodingMetaData/GeocodingMetaData.bundle/vi.db -------------------------------------------------------------------------------- /libPhoneNumberGeocodingMetaData/GeocodingMetaData.bundle/zh.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iziz/libPhoneNumber-iOS/HEAD/libPhoneNumberGeocodingMetaData/GeocodingMetaData.bundle/zh.db -------------------------------------------------------------------------------- /libPhoneNumberInternal/_HeaderOnlyShim.c: -------------------------------------------------------------------------------- 1 | // 2 | // _HeaderOnlyShim.c 3 | // libPhoneNumber 4 | // 5 | // Created by Kris Kline on 11/6/25. 6 | // 7 | 8 | #include 9 | -------------------------------------------------------------------------------- /libPhoneNumberTestsCommon/libPhoneNumberMetaDataForTesting.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iziz/libPhoneNumber-iOS/HEAD/libPhoneNumberTestsCommon/libPhoneNumberMetaDataForTesting.zip -------------------------------------------------------------------------------- /libPhoneNumberGeocodingMetaData/GeocodingMetaData.bundle/zh_Hant.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iziz/libPhoneNumber-iOS/HEAD/libPhoneNumberGeocodingMetaData/GeocodingMetaData.bundle/zh_Hant.db -------------------------------------------------------------------------------- /libPhoneNumberGeocodingMetaData/_HeaderOnlyShim.c: -------------------------------------------------------------------------------- 1 | // 2 | // _HeaderOnlyShim.c 3 | // libPhoneNumber 4 | // 5 | // Created by Kris Kline on 11/6/25. 6 | // 7 | 8 | #include 9 | -------------------------------------------------------------------------------- /libPhoneNumberShortNumberInternal/_HeaderOnlyShim.c: -------------------------------------------------------------------------------- 1 | // 2 | // _HeaderOnlyShim.c 3 | // libPhoneNumber 4 | // 5 | // Created by Kris Kline on 11/6/25. 6 | // 7 | 8 | #include 9 | -------------------------------------------------------------------------------- /libPhoneNumber-Demo/libPhoneNumber-Demo/Assets.xcassets/AppIcon.appiconset/1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iziz/libPhoneNumber-iOS/HEAD/libPhoneNumber-Demo/libPhoneNumber-Demo/Assets.xcassets/AppIcon.appiconset/1024.png -------------------------------------------------------------------------------- /libPhoneNumberGeocodingMetaData/_EmptyHeader.h: -------------------------------------------------------------------------------- 1 | // 2 | // _EmptyHeader.h 3 | // libPhoneNumber 4 | // 5 | // Created by Kris Kline on 11/7/25. 6 | // 7 | 8 | #ifndef Header_h 9 | #define Header_h 10 | 11 | 12 | #endif /* Header_h */ 13 | -------------------------------------------------------------------------------- /libPhoneNumber.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /libPhoneNumber-Demo/libPhoneNumber-Demo-SPM.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /libPhoneNumber-Demo/libPhoneNumber-Demo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /libPhoneNumber-GeocodingParser/Podfile: -------------------------------------------------------------------------------- 1 | platform :macos, '15' 2 | 3 | target 'libPhoneNumber-GeocodingParser' do 4 | # Comment the next line if you don't want to use dynamic frameworks 5 | #use_frameworks! 6 | 7 | # Pods for libPhoneNumber-GeocodingParser 8 | pod 'SSZipArchive' 9 | end 10 | -------------------------------------------------------------------------------- /libPhoneNumber-GeocodingParser/libPhoneNumber-GeocodingParser.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /libPhoneNumber.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /libPhoneNumber/NBPhoneNumberDefines.m: -------------------------------------------------------------------------------- 1 | #import "NBPhoneNumberDefines.h" 2 | 3 | NSString* const NB_UNKNOWN_REGION = @"ZZ"; 4 | NSString* const NB_NON_BREAKING_SPACE = @"\u00a0"; 5 | NSString* const NB_PLUS_CHARS = @"++"; 6 | NSString* const NB_VALID_DIGITS_STRING = @"0-90-9٠-٩۰-۹"; 7 | NSString* const NB_REGION_CODE_FOR_NON_GEO_ENTITY = @"001"; 8 | -------------------------------------------------------------------------------- /libPhoneNumber-Demo/libPhoneNumber-Demo.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /libPhoneNumber-Demo/libPhoneNumber-Demo.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /libPhoneNumber-Demo/libPhoneNumber-Demo/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "1024.png", 5 | "idiom" : "universal", 6 | "platform" : "ios", 7 | "size" : "1024x1024" 8 | } 9 | ], 10 | "info" : { 11 | "author" : "xcode", 12 | "version" : 1 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /libPhoneNumber-Demo/libPhoneNumber-Demo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /libPhoneNumber-Demo/libPhoneNumber-Demo-SPM.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /libPhoneNumber-GeocodingParser/libPhoneNumber-GeocodingParser.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /libPhoneNumber.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /libPhoneNumber-GeocodingParser/libPhoneNumber-GeocodingParser.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /libPhoneNumber-GeocodingParser/libPhoneNumber-GeocodingParser.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.slather.yml: -------------------------------------------------------------------------------- 1 | ci_service: travis_ci 2 | coverage_service: coveralls 3 | xcodeproj: libPhoneNumber.xcodeproj 4 | ignore: 5 | - libPhoneNumber/AppDelegate.m 6 | - libPhoneNumber/main.m 7 | - libPhoneNumber/NBPhoneMetaDataGenerator.m 8 | - libPhoneNumber/NBPhoneMetaData.m 9 | - libPhoneNumber/NBPhoneNumberDesc.m 10 | - libPhoneNumber/NBMetadataHelper.m 11 | - libPhoneNumber/NBNumberFormat.m 12 | - libPhoneNumber/NBPhoneNumber.m 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OS X 2 | .DS_Store 3 | 4 | # Xcode 5 | build/ 6 | *.pbxuser 7 | !default.pbxuser 8 | *.mode1v3 9 | !default.mode1v3 10 | *.mode2v3 11 | !default.mode2v3 12 | *.perspectivev3 13 | !default.perspectivev3 14 | xcuserdata 15 | *.xccheckout 16 | profile 17 | *.moved-aside 18 | DerivedData 19 | *.hmap 20 | *.ipa 21 | 22 | # CocoaPods 23 | Pods 24 | Podfile.lock 25 | ObjectiveC.gcda 26 | 27 | *.gcno 28 | 29 | # SPM 30 | .build 31 | .swiftpm 32 | -------------------------------------------------------------------------------- /libPhoneNumber/PrivacyInfo.xcprivacy: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NSPrivacyTracking 6 | 7 | NSPrivacyCollectedDataTypes 8 | 9 | NSPrivacyTrackingDomains 10 | 11 | NSPrivacyAccessedAPITypes 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /libPhoneNumberTestsCommon/NBTestingMetaData.h: -------------------------------------------------------------------------------- 1 | /***** 2 | * Data Generated from GeneratePhoneNumberHeader.sh 3 | * Off of PhoneNumberMetaDataForTesting.json 4 | */ 5 | 6 | #include 7 | 8 | // z_const is not defined in some versions of zlib, so define it here 9 | // in case it has not been defined. 10 | #if defined(ZLIB_CONST) && !defined(z_const) 11 | # define z_const const 12 | #else 13 | # define z_const 14 | #endif 15 | 16 | extern z_const size_t kPhoneNumberMetaDataForTestingExpandedLength; 17 | -------------------------------------------------------------------------------- /libPhoneNumber/NSArray+NBAdditions.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSArray+NBAdditions.h 3 | // libPhoneNumber 4 | // 5 | // Created by Dave MacLachlan on 2/7/17. 6 | // Copyright © 2017 ohtalk.me. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NSArray (NBAdditions) 12 | - (id)nb_safeObjectAtIndex:(NSUInteger)index class:(Class)clazz; 13 | - (NSString *)nb_safeStringAtIndex:(NSUInteger)index; 14 | - (NSNumber *)nb_safeNumberAtIndex:(NSUInteger)index; 15 | - (NSArray *)nb_safeArrayAtIndex:(NSUInteger)index; 16 | - (NSData *)nb_safeDataAtIndex:(NSUInteger)index; 17 | @end 18 | -------------------------------------------------------------------------------- /libPhoneNumberInternal/NBGeneratedPhoneNumberMetaData.h: -------------------------------------------------------------------------------- 1 | /***** 2 | * Data Generated from GeneratePhoneNumberHeader.sh 3 | * Off of PhoneNumberMetaData.json 4 | */ 5 | 6 | #include 7 | 8 | // z_const is not defined in some versions of zlib, so define it here 9 | // in case it has not been defined. 10 | #if defined(ZLIB_CONST) && !defined(z_const) 11 | # define z_const const 12 | #else 13 | # define z_const 14 | #endif 15 | 16 | extern z_const Bytef kPhoneNumberMetaData[]; 17 | extern z_const size_t kPhoneNumberMetaDataCompressedLength; 18 | extern z_const size_t kPhoneNumberMetaDataExpandedLength; 19 | -------------------------------------------------------------------------------- /libPhoneNumberTests/libPhoneNumberTests.xctestplan: -------------------------------------------------------------------------------- 1 | { 2 | "configurations" : [ 3 | { 4 | "id" : "815C4BDB-0588-41AF-B5E6-02ED291715F8", 5 | "name" : "Test Scheme Action", 6 | "options" : { 7 | 8 | } 9 | } 10 | ], 11 | "defaultOptions" : { 12 | "testExecutionOrdering" : "random" 13 | }, 14 | "testTargets" : [ 15 | { 16 | "parallelizable" : false, 17 | "target" : { 18 | "containerPath" : "container:", 19 | "identifier" : "libPhoneNumberTests", 20 | "name" : "libPhoneNumberTests" 21 | } 22 | } 23 | ], 24 | "version" : 1 25 | } 26 | -------------------------------------------------------------------------------- /libPhoneNumberShortNumberInternal/NBGeneratedShortNumberMetaData.h: -------------------------------------------------------------------------------- 1 | /***** 2 | * Data Generated from GeneratePhoneNumberHeader.sh 3 | * Off of ShortNumberMetaData.json 4 | */ 5 | 6 | #include 7 | 8 | // z_const is not defined in some versions of zlib, so define it here 9 | // in case it has not been defined. 10 | #if defined(ZLIB_CONST) && !defined(z_const) 11 | # define z_const const 12 | #else 13 | # define z_const 14 | #endif 15 | 16 | extern z_const Bytef kShortNumberMetaData[]; 17 | extern z_const size_t kShortNumberMetaDataCompressedLength; 18 | extern z_const size_t kShortNumberMetaDataExpandedLength; 19 | -------------------------------------------------------------------------------- /libPhoneNumberShortNumberTests/libPhoneNumberShortNumberTests.xctestplan: -------------------------------------------------------------------------------- 1 | { 2 | "configurations" : [ 3 | { 4 | "id" : "B7A44DCB-ACC9-4889-B687-F16C19F926BD", 5 | "name" : "Configuration 1", 6 | "options" : { 7 | 8 | } 9 | } 10 | ], 11 | "defaultOptions" : { 12 | "testExecutionOrdering" : "random" 13 | }, 14 | "testTargets" : [ 15 | { 16 | "parallelizable" : false, 17 | "target" : { 18 | "containerPath" : "container:", 19 | "identifier" : "libPhoneNumberShortNumberTests", 20 | "name" : "libPhoneNumberShortNumberTests" 21 | } 22 | } 23 | ], 24 | "version" : 1 25 | } 26 | -------------------------------------------------------------------------------- /libPhoneNumberGeocodingTests/libPhoneNumberGeocodingTests.xctestplan: -------------------------------------------------------------------------------- 1 | { 2 | "configurations" : [ 3 | { 4 | "id" : "96F5CCB0-B4CD-4575-9027-153C7B2B6498", 5 | "name" : "Test Scheme Action", 6 | "options" : { 7 | 8 | } 9 | } 10 | ], 11 | "defaultOptions" : { 12 | "codeCoverage" : false, 13 | "testExecutionOrdering" : "random" 14 | }, 15 | "testTargets" : [ 16 | { 17 | "parallelizable" : false, 18 | "target" : { 19 | "containerPath" : "container:", 20 | "identifier" : "libPhoneNumberGeocodingTests", 21 | "name" : "libPhoneNumberGeocodingTests" 22 | } 23 | } 24 | ], 25 | "version" : 1 26 | } 27 | -------------------------------------------------------------------------------- /libPhoneNumberGeocoding/libPhoneNumberGeocoding.h: -------------------------------------------------------------------------------- 1 | // 2 | // libPhoneNumberGeocoding.h 3 | // Geocoding 4 | // 5 | // Created by Frank Itthipalkul on 6/23/20. 6 | // Copyright © 2020 Google LLC. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for Geocoding. 12 | FOUNDATION_EXPORT double GeocodingVersionNumber; 13 | 14 | //! Project version string for Geocoding. 15 | FOUNDATION_EXPORT const unsigned char GeocodingVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework 18 | // using statements like #import 19 | 20 | #import "NBGeocoderMetaDataHelper.h" 21 | #import "NBPhoneNumberOfflineGeocoder.h" 22 | -------------------------------------------------------------------------------- /libPhoneNumberShortNumberTests/NBShortNumberTestHelper.h: -------------------------------------------------------------------------------- 1 | // 2 | // NBShortNumberTestHelper.h 3 | // libPhoneNumberShortNumber 4 | // 5 | // Created by Paween Itthipalkul on 12/1/17. 6 | // Copyright © 2017 Google LLC. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "NBPhoneNumberUtil.h" 11 | #import "NBShortNumberUtil.h" 12 | 13 | NS_ASSUME_NONNULL_BEGIN 14 | 15 | /** 16 | Includes methods used only for testing NBPhoneNumberUtil+ShortNumber. 17 | */ 18 | @interface NBShortNumberTestHelper : NSObject 19 | 20 | - (NSString *)exampleShortNumberForCost:(NBEShortNumberCost)cost regionCode:(NSString *)regionCode; 21 | - (NSString *)exampleShortNumberWithRegionCode:(NSString *)regionCode; 22 | 23 | @end 24 | 25 | NS_ASSUME_NONNULL_END 26 | -------------------------------------------------------------------------------- /libPhoneNumber-Demo/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment the next line to define a global platform for your project 2 | platform :ios, '13.5' 3 | 4 | project 'libPhoneNumber-Demo' 5 | 6 | target 'libPhoneNumber-Demo' do 7 | # Comment the next line if you don't want to use dynamic frameworks 8 | use_frameworks! 9 | 10 | # Pods for libPhoneNumber-Demo 11 | #pod 'libPhoneNumber-iOS', :git => 'https://github.com/iziz/libPhoneNumber-iOS' 12 | #pod 'libPhoneNumberGeocoding', :git => 'https://github.com/iziz/libPhoneNumber-iOS' 13 | #pod 'libPhoneNumberShortNumber', :git => 'https://github.com/iziz/libPhoneNumber-iOS' 14 | pod 'libPhoneNumber-iOS', :path => '../' 15 | pod 'libPhoneNumberGeocoding', :path => '../' 16 | pod 'libPhoneNumberShortNumber', :path => '../' 17 | 18 | end 19 | -------------------------------------------------------------------------------- /libPhoneNumberTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /libPhoneNumber-GeocodingParser/libPhoneNumber-GeocodingParser/NBGeocoderMetadataParser.h: -------------------------------------------------------------------------------- 1 | // 2 | // NBGeocoderMetadataParser.h 3 | // libPhoneNumber-GeocodingParser 4 | // 5 | // Created by Rastaar Haghi on 7/1/20. 6 | // Copyright © 2020 Google LLC. All rights reserved. 7 | // 8 | 9 | #import "NBSQLiteDatabaseConnection.h" 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface NBGeocoderMetadataParser : NSObject 14 | 15 | - (instancetype)initWithDestinationPath:(NSURL *)desiredDatabaseLocation; 16 | 17 | - (void)convertFileToSQLiteDatabase:(NSString *)completeTextFilePath 18 | withFileName:(NSString *)textFileName 19 | withLanguage:(NSString *)languageCode 20 | loggingIndent:(NSString *)indent; 21 | @end 22 | 23 | NS_ASSUME_NONNULL_END 24 | -------------------------------------------------------------------------------- /libPhoneNumber-Demo/libPhoneNumber-Demo/GeocodingView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GeocodingView.swift 3 | // libPhoneNumber-Demo 4 | // 5 | // Created by Rastaar Haghi on 7/17/20. 6 | // Copyright © 2020 Google LLC. All rights reserved. 7 | // 8 | 9 | import SwiftUI 10 | 11 | struct GeocodingView: View { 12 | var body: some View { 13 | List { 14 | NavigationLink(destination: GeocodingTableView()) { 15 | Text("Table of Region Descriptions") 16 | } 17 | NavigationLink(destination: GeocodingSearchView()) { 18 | Text("Search for a phone number") 19 | } 20 | } 21 | .navigationBarTitle("Geocoding", displayMode: .inline) 22 | } 23 | } 24 | 25 | struct GeocodingView_Previews: PreviewProvider { 26 | static var previews: some View { 27 | GeocodingView() 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /libPhoneNumberShortNumber/libPhoneNumberShortNumber.h: -------------------------------------------------------------------------------- 1 | // 2 | // libPhoneNumberShortNumber.h 3 | // libPhoneNumberShortNumber 4 | // 5 | // Created by Rastaar Haghi on 7/14/20. 6 | // Copyright © 2020 Google. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for libPhoneNumberShortNumber. 12 | FOUNDATION_EXPORT double libPhoneNumberShortNumberVersionNumber; 13 | 14 | //! Project version string for libPhoneNumberShortNumber. 15 | FOUNDATION_EXPORT const unsigned char libPhoneNumberShortNumberVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework 18 | // using statements like #import 19 | #import "NBShortNumberMetadataHelper.h" 20 | #import "NBShortNumberUtil.h" 21 | -------------------------------------------------------------------------------- /libPhoneNumber-GeocodingParser/libPhoneNumber-GeocodingParser/NBSQLiteDatabaseConnection.h: -------------------------------------------------------------------------------- 1 | // 2 | // NBSQLiteDatabaseConnection.h 3 | // libPhoneNumber-GeocodingParser 4 | // 5 | // Created by Rastaar Haghi on 7/1/20. 6 | // Copyright © 2020 Google LLC. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | @interface NBSQLiteDatabaseConnection : NSObject 15 | 16 | - (instancetype)initWithCountryCode:(NSString *)countryCode 17 | withLanguage:(NSString *)language 18 | withDestinationPath:(NSURL *)destinationPath; 19 | 20 | - (void)addEntryToDB:(NSString *)phoneNumber 21 | withDescription:(NSString *)description 22 | withCountryCode:(NSString *)countryCode; 23 | @end 24 | 25 | NS_ASSUME_NONNULL_END 26 | -------------------------------------------------------------------------------- /libPhoneNumberGeocodingTests/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 | -------------------------------------------------------------------------------- /libPhoneNumber-GeocodingParser/libPhoneNumber-GeocodingParser/NSString+Extensions.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+Extensions.h 3 | // libPhoneNumber-GeocodingParser 4 | // 5 | // Created by Kris Kline on 11/12/25. 6 | // Copyright © 2025 Rastaar Haghi. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NSString (Extensions) 12 | 13 | /** 14 | @returns: Whether this string represents a numbered version: X.Y.Z, X.Y, or Z 15 | */ 16 | - (BOOL)isVersion; 17 | 18 | /** 19 | Removes any occurrence of the specific strings from this string and returns the new string 20 | - parameter substrings: The strings to remove from this string 21 | 22 | - returns: The new string without any occurrences of the specified strings 23 | */ 24 | - (NSString*)removeAnyOccurrencesOfStrings:(NSArray*)substrings; 25 | 26 | @end 27 | -------------------------------------------------------------------------------- /libPhoneNumberShortNumberTests/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 | -------------------------------------------------------------------------------- /libPhoneNumberGeocoding/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 | $(CURRENT_PROJECT_VERSION) 21 | 22 | 23 | -------------------------------------------------------------------------------- /libPhoneNumberShortNumber/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 | $(CURRENT_PROJECT_VERSION) 21 | 22 | 23 | -------------------------------------------------------------------------------- /libPhoneNumber/NBPhoneNumberDesc.h: -------------------------------------------------------------------------------- 1 | // 2 | // NBPhoneNumberDesc.h 3 | // libPhoneNumber 4 | // 5 | // 6 | 7 | #import 8 | 9 | @interface NBPhoneNumberDesc : NSObject 10 | 11 | // from phonemetadata.pb.js 12 | /* 2 */ @property(nonatomic, strong, readonly) NSString *nationalNumberPattern; 13 | /* 3 */ @property(nonatomic, strong, readonly) NSString *possibleNumberPattern; 14 | /* 9 */ @property(nonatomic, strong, readonly) NSArray *possibleLength; 15 | /* 10 */ @property(nonatomic, strong, readonly) NSArray *possibleLengthLocalOnly; 16 | /* 6 */ @property(nonatomic, strong, readonly) NSString *exampleNumber; 17 | /* 7 */ @property(nonatomic, strong, readonly) NSData *nationalNumberMatcherData; 18 | /* 8 */ @property(nonatomic, strong, readonly) NSData *possibleNumberMatcherData; 19 | 20 | - (instancetype)initWithEntry:(NSArray *)entry; 21 | @end 22 | -------------------------------------------------------------------------------- /libPhoneNumber-GeocodingParser/libPhoneNumber-GeocodingParser/ArgumentParser.h: -------------------------------------------------------------------------------- 1 | // 2 | // ArgumentParser.h 3 | // libPhoneNumber-GeocodingParser 4 | // 5 | // Created by Kris Kline on 11/12/25. 6 | // Copyright © 2025 Rastaar Haghi. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | /** 12 | Struct to represent the command line arguments 13 | */ 14 | typedef struct { 15 | /// The directory to output all the language specific databases to 16 | NSString *directory; 17 | 18 | /// The version of metadata to download ('X.Y.Z' or 'master') 19 | NSString *version; 20 | } ArgResult; 21 | 22 | /** 23 | Parses the command arguments passed to this program 24 | 25 | - parameter argc: The argument count 26 | - parameter argv: The array of arguments 27 | 28 | - returns: An ArgResult representing the arguments passed to this program 29 | */ 30 | ArgResult *parseArguments(int argc, const char * argv[]); 31 | -------------------------------------------------------------------------------- /libPhoneNumberInternal/NBRegExMatcher.h: -------------------------------------------------------------------------------- 1 | // 2 | // NBRegExMatcher.h 3 | // libPhoneNumber 4 | // 5 | // Created by Paween Itthipalkul on 11/29/17. 6 | // Copyright © 2017 Google LLC. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class NBPhoneNumberDesc; 12 | 13 | @interface NBRegExMatcher : NSObject 14 | 15 | /** 16 | Returns whether the given national number (a string containing only decimal digits) matches 17 | the national number pattern defined in the given {@code PhoneNumberDesc} message. 18 | 19 | @param string National number string ot match. 20 | @param numberDesc Phone number description. 21 | @param allowsPrefixMatch Whether to allow prefix match or not. 22 | @return Whether the given national number matches the pattern. 23 | */ 24 | - (BOOL)matchNationalNumber:(NSString *)string 25 | phoneNumberDesc:(NBPhoneNumberDesc *)numberDesc 26 | allowsPrefixMatch:(BOOL)allowsPrefixMatch; 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /libPhoneNumber/NBMetadataHelper.h: -------------------------------------------------------------------------------- 1 | // 2 | // NBMetadataHelper.h 3 | // libPhoneNumber 4 | // 5 | // Created by tabby on 2015. 2. 8.. 6 | // Copyright (c) 2015년 ohtalk.me. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "NBPhoneNumberDefines.h" 11 | 12 | @class NBPhoneMetaData; 13 | 14 | @interface NBMetadataHelper : NSObject 15 | 16 | + (BOOL)hasValue:(NSString *)string; 17 | 18 | - (instancetype)initWithZippedData:(NSData *)data expandedLength:(NSUInteger)expandedLength; 19 | 20 | - (instancetype)init; 21 | 22 | - (NSArray *)regionCodeFromCountryCode:(NSNumber *)countryCodeNumber; 23 | - (NSString *)countryCodeFromRegionCode:(NSString *)regionCode; 24 | 25 | - (NBPhoneMetaData *)getMetadataForNonGeographicalRegion:(NSNumber *)countryCallingCode; 26 | - (NBPhoneMetaData *)getMetadataForRegion:(NSString *)regionCode; 27 | 28 | - (NSDictionary *)countryCodeToCountryNumberDictionary; 29 | - (NSArray *)getAllMetadata; 30 | 31 | @end 32 | -------------------------------------------------------------------------------- /libPhoneNumber/NBAsYouTypeFormatter.h: -------------------------------------------------------------------------------- 1 | // 2 | // NBAsYouTypeFormatter.h 3 | // libPhoneNumber 4 | // 5 | // Created by ishtar on 13. 2. 25.. 6 | // 7 | 8 | #import 9 | 10 | @class NBAsYouTypeFormatter; 11 | @class NBMetadataHelper; 12 | 13 | @interface NBAsYouTypeFormatter : NSObject 14 | 15 | - (instancetype)initWithRegionCode:(NSString *)regionCode; 16 | - (instancetype)initWithRegionCode:(NSString *)regionCode metadataHelper:(NBMetadataHelper *)helper; 17 | 18 | - (NSString *)inputString:(NSString *)string; 19 | - (NSString *)inputStringAndRememberPosition:(NSString *)string; 20 | 21 | - (NSString *)inputDigit:(NSString *)nextChar; 22 | - (NSString *)inputDigitAndRememberPosition:(NSString *)nextChar; 23 | 24 | - (NSString *)removeLastDigit; 25 | - (NSString *)removeLastDigitAndRememberPosition; 26 | 27 | - (NSInteger)getRememberedPosition; 28 | 29 | - (void)clear; 30 | 31 | @property(nonatomic, assign, readonly) BOOL isSuccessfulFormatting; 32 | 33 | @end 34 | -------------------------------------------------------------------------------- /libPhoneNumber/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | libPhoneNumberiOS 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | FMWK 19 | CFBundleShortVersionString 20 | $(MARKETING_VERSION) 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | $(CURRENT_PROJECT_VERSION) 25 | NSPrincipalClass 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /libPhoneNumber/NBPhoneNumber.h: -------------------------------------------------------------------------------- 1 | // 2 | // NBPhoneNumber.h 3 | // libPhoneNumber 4 | // 5 | // 6 | 7 | #import 8 | #import "NBPhoneNumberDefines.h" 9 | 10 | @interface NBPhoneNumber : NSObject 11 | 12 | // from phonemetadata.pb.js 13 | /* 1 */ @property(nonatomic, strong, readwrite) NSNumber *countryCode; 14 | /* 2 */ @property(nonatomic, strong, readwrite) NSNumber *nationalNumber; 15 | /* 3 */ @property(nonatomic, strong, readwrite) NSString *extension; 16 | /* 4 */ @property(nonatomic, assign, readwrite) BOOL italianLeadingZero; 17 | /* 8 */ @property(nonatomic, strong, readwrite) NSNumber *numberOfLeadingZeros; 18 | /* 5 */ @property(nonatomic, strong, readwrite) NSString *rawInput; 19 | /* 6 */ @property(nonatomic, strong, readwrite) NSNumber *countryCodeSource; 20 | /* 7 */ @property(nonatomic, strong, readwrite) NSString *preferredDomesticCarrierCode; 21 | 22 | - (void)clearCountryCodeSource; 23 | - (NBECountryCodeSource)getCountryCodeSourceOrDefault; 24 | 25 | @end 26 | -------------------------------------------------------------------------------- /libPhoneNumberShortNumber/NBShortNumberMetadataHelper.h: -------------------------------------------------------------------------------- 1 | // 2 | // NBShortNumberMetadataHelper.h 3 | // libPhoneNumberShortNumber 4 | // 5 | // Created by Rastaar Haghi on 7/15/20. 6 | // Copyright © 2020 Google. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class NBPhoneMetaData; 12 | @class NBMetadataHelper; 13 | 14 | NS_ASSUME_NONNULL_BEGIN 15 | 16 | @interface NBShortNumberMetadataHelper : NSObject 17 | 18 | - (instancetype)initWithZippedData:(NSData *)data 19 | expandedLength:(NSUInteger)expandedLength 20 | metadataHelper:(NBMetadataHelper *)helper; 21 | 22 | /** 23 | * Returns the short number metadata for the given region code or {@code nil} if the region 24 | * code is invalid or unknown. 25 | * 26 | * @param regionCode regionCode 27 | * @return {i18n.phonenumbers.PhoneMetadata} 28 | */ 29 | - (NBPhoneMetaData * _Nullable)shortNumberMetadataForRegion:(NSString * _Nonnull)regionCode; 30 | 31 | @end 32 | 33 | NS_ASSUME_NONNULL_END 34 | -------------------------------------------------------------------------------- /libPhoneNumber-Demo/libPhoneNumber-Demo/MenuView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContentView.swift 3 | // libPhoneNumber-Demo 4 | // 5 | // Created by Rastaar Haghi on 7/17/20. 6 | // Copyright © 2020 Google LLC. All rights reserved. 7 | // 8 | 9 | import SwiftUI 10 | 11 | struct MenuView: View { 12 | var body: some View { 13 | NavigationView { 14 | List { 15 | NavigationLink(destination: GeocodingView()) { 16 | Text("Geocoding") 17 | } 18 | NavigationLink(destination: PhoneUtilView()) { 19 | Text("Phone Number Parser") 20 | } 21 | NavigationLink(destination: ShortNumberUtilView()) { 22 | Text("Short Number Formatter") 23 | } 24 | NavigationLink(destination: FormatterView()) { 25 | Text("Phone Number Formatter") 26 | } 27 | } 28 | .navigationBarTitle("libPhoneNumber-iOS") 29 | } 30 | } 31 | } 32 | 33 | struct ContentView_Previews: PreviewProvider { 34 | static var previews: some View { 35 | MenuView() 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /libPhoneNumber/libPhoneNumber.h: -------------------------------------------------------------------------------- 1 | // 2 | // libPhoneNumber.h 3 | // libPhoneNumber 4 | // 5 | // Created by Roy Marmelstein on 04/08/2015. 6 | // Copyright (c) 2015 ohtalk.me. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for libPhoneNumber-iOS. 12 | FOUNDATION_EXPORT double libPhoneNumber_iOSVersionNumber; 13 | 14 | //! Project version string for libPhoneNumber-iOS. 15 | FOUNDATION_EXPORT const unsigned char libPhoneNumber_iOSVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework 18 | // using statements like #import 19 | 20 | #import "NBPhoneNumberDefines.h" 21 | 22 | // Features 23 | #import "NBAsYouTypeFormatter.h" 24 | #import "NBPhoneNumberUtil.h" 25 | 26 | // Metadata 27 | #import "NBMetadataHelper.h" 28 | 29 | // Model 30 | #import "NBNumberFormat.h" 31 | #import "NBPhoneMetaData.h" 32 | #import "NBPhoneNumber.h" 33 | #import "NBPhoneNumberDesc.h" 34 | 35 | #import "NSArray+NBAdditions.h" 36 | -------------------------------------------------------------------------------- /libPhoneNumberInternal/NBRegularExpressionCache.h: -------------------------------------------------------------------------------- 1 | // 2 | // NBRegularExpressionCache.h 3 | // libPhoneNumber 4 | // 5 | // Created by Paween Itthipalkul on 11/29/17. 6 | // Copyright © 2017 Google LLC. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface NBRegularExpressionCache : NSObject 14 | 15 | /** 16 | Returns a singleton instance of the regular expression cache. 17 | 18 | @return An instance of NBRegularExpressionCache 19 | */ 20 | + (instancetype)sharedInstance; 21 | 22 | /** 23 | Returns compiled regular expression for a given pattern. 24 | 25 | @param pattern Regular expression pattern. 26 | @param error If an error occurs, upon returns contains an NSError object that describes 27 | the problem. If you are not interested in possible errors, pass in NULL. 28 | @return A regular expression. 29 | */ 30 | - (nullable NSRegularExpression *)regularExpressionForPattern:(NSString *)pattern 31 | error:(NSError * _Nullable *)error; 32 | 33 | @end 34 | 35 | NS_ASSUME_NONNULL_END 36 | -------------------------------------------------------------------------------- /libPhoneNumber/NSArray+NBAdditions.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSArray+NBAdditions.m 3 | // libPhoneNumber 4 | // 5 | // Created by Dave MacLachlan on 2/7/17. 6 | // Copyright © 2017 ohtalk.me. All rights reserved. 7 | // 8 | 9 | #import "NSArray+NBAdditions.h" 10 | 11 | @implementation NSArray (NBAdditions) 12 | 13 | - (id)nb_safeObjectAtIndex:(NSUInteger)index class:(Class)clazz { 14 | if (index >= self.count) { 15 | return nil; 16 | } 17 | id res = [self objectAtIndex:index]; 18 | if (![res isKindOfClass:clazz]) { 19 | return nil; 20 | } 21 | return res; 22 | } 23 | 24 | - (NSString *)nb_safeStringAtIndex:(NSUInteger)index { 25 | return [self nb_safeObjectAtIndex:index class:[NSString class]]; 26 | } 27 | 28 | - (NSNumber *)nb_safeNumberAtIndex:(NSUInteger)index { 29 | return [self nb_safeObjectAtIndex:index class:[NSNumber class]]; 30 | } 31 | - (NSArray *)nb_safeArrayAtIndex:(NSUInteger)index { 32 | return [self nb_safeObjectAtIndex:index class:[NSArray class]]; 33 | } 34 | 35 | - (NSData *)nb_safeDataAtIndex:(NSUInteger)index { 36 | return [self nb_safeObjectAtIndex:index class:[NSData class]]; 37 | } 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /libPhoneNumber-Demo/README.md: -------------------------------------------------------------------------------- 1 | # **libPhoneNumber-iOS Demo App** 2 | 3 | This iOS application project provides demonstrations on how to implement key features of libPhoneNumber-iOS, 4 | libPhoneNumberShortNumber, and libPhoneNumberGeocoding. 5 | 6 | The default project - libPhoneNumber-Demo uses Cocoapods to download the open-source projects associated with libPhoneNumber-iOS. 7 | 8 | The SPM project - libPhoneNumber-Demo-SPM uses Swift Package Manager to pull in the open-source projects associated with libPhoneNumber-iOS. 9 | 10 | Both projects rely on the same source code and were made using SwiftUI. 11 | 12 | ## Usage (Cocoapods) 13 | 14 | Before running the application, you must install the Cocoapods that are defined in `Podfile`. 15 | 16 | 1) Navigate to the `Podfile` in your terminal system. (Ex: `../libPhoneNumber-iOS/libPhoneNumber-Demo/` ) 17 | 2) Run the following command to install the necessary pods 18 | ``` 19 | pod install 20 | ``` 21 | 22 | ### Run the project on libPhoneNumber-Demo.xcworkspace 23 | 24 | 1) Open the project using the Xcode Workspace file to access the complete project with its integrated Cocoapods. 25 | 2) Build the libPhoneNumber-Demo and press Run. 26 | -------------------------------------------------------------------------------- /libPhoneNumberTestsCommon/NSBundle+Extensions.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSBundle+Extensions.h 3 | // libPhoneNumber 4 | // 5 | // Created by Kris Kline on 11/5/25. 6 | // 7 | 8 | #import 9 | 10 | @interface NSBundle (PathForFirst) 11 | 12 | /** 13 | * Searches through all of the bundles and returns the path for the first resource with the specified name and type found 14 | * - NOTE: This means the behavior is undefined if there are multiple files with the same name located in different bundles of this application 15 | * 16 | * - parameter name: The name of the file to look for: . 17 | * - parameter type: The type of the file to look for: . 18 | * 19 | * - returns The path to the found resource, nil if the specified resource couldn't be found in any bundles 20 | */ 21 | +(nullable NSString*)pathForFirstResourceNamed:(nonnull NSString*)name ofType:(nullable NSString*)type; 22 | 23 | /** 24 | * Return an array of child bundles within this bundle. Nil if no child bundles 25 | * 26 | * - returns An array of child bundles within this bundle. Nil if no child bundles exist. 27 | */ 28 | -(nullable NSArray*)childBundles; 29 | 30 | @end 31 | -------------------------------------------------------------------------------- /libPhoneNumber/NBNumberFormat.h: -------------------------------------------------------------------------------- 1 | // 2 | // NBPhoneNumberFormat.h 3 | // libPhoneNumber 4 | // 5 | // 6 | 7 | #import 8 | 9 | @interface NBNumberFormat : NSObject 10 | 11 | // from phonemetadata.pb.js 12 | /* 1 */ @property(nonatomic, strong) NSString *pattern; 13 | /* 2 */ @property(nonatomic, strong) NSString *format; 14 | /* 3 */ @property(nonatomic, strong) NSArray *leadingDigitsPatterns; 15 | /* 4 */ @property(nonatomic, strong) NSString *nationalPrefixFormattingRule; 16 | /* 6 */ @property(nonatomic, assign) BOOL nationalPrefixOptionalWhenFormatting; 17 | /* 5 */ @property(nonatomic, strong) NSString *domesticCarrierCodeFormattingRule; 18 | 19 | - (instancetype)initWithPattern:(NSString *)pattern 20 | withFormat:(NSString *)format 21 | withLeadingDigitsPatterns:(NSArray *)leadingDigitsPatterns 22 | withNationalPrefixFormattingRule:(NSString *)nationalPrefixFormattingRule 23 | whenFormatting:(BOOL)nationalPrefixOptionalWhenFormatting 24 | withDomesticCarrierCodeFormattingRule:(NSString *)domesticCarrierCodeFormattingRule; 25 | - (instancetype)initWithEntry:(NSArray *)entry; 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /libPhoneNumber-GeocodingParser/libPhoneNumber-GeocodingParser/NSString+Extensions.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+Extensions.m 3 | // libPhoneNumber-GeocodingParser 4 | // 5 | // Created by Kris Kline on 11/12/25. 6 | // Copyright © 2025 Rastaar Haghi. All rights reserved. 7 | // 8 | 9 | #import "NSString+Extensions.h" 10 | 11 | @implementation NSString(Extensions) 12 | 13 | - (BOOL)isVersion { 14 | static NSRegularExpression* versionRegex = nil; 15 | static dispatch_once_t onceToken; 16 | dispatch_once(&onceToken, ^{ 17 | versionRegex = [NSRegularExpression regularExpressionWithPattern:@"^\\d+(\\.\\d+){0,2}$" 18 | options:0 19 | error:nil]; 20 | }); 21 | NSRange range = NSMakeRange(0, self.length); 22 | NSUInteger matchCount = [versionRegex numberOfMatchesInString:self options:0 range:range]; 23 | return matchCount == 1; 24 | } 25 | 26 | - (NSString*)removeAnyOccurrencesOfStrings:(NSArray *)substrings { 27 | NSString* result = [self copy]; 28 | for (NSString* substr in substrings) { 29 | result = [result stringByReplacingOccurrencesOfString:substr withString:@""]; 30 | } 31 | return result; 32 | } 33 | @end 34 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: objective-c 2 | 3 | before_install: 4 | - gem install slather -N 5 | 6 | script: 7 | - xcodebuild -project libPhoneNumber.xcodeproj -scheme libPhoneNumberiOS clean -sdk iphonesimulator 8 | - xcodebuild -project libPhoneNumber.xcodeproj -scheme libPhoneNumberiOS -sdk iphonesimulator ONLY_ACTIVE_ARCH=NO GCC_INSTRUMENT_PROGRAM_FLOW_ARCS=YES build-for-testing 9 | - xctool -project libPhoneNumber.xcodeproj -scheme libPhoneNumberiOS run-tests -sdk iphonesimulator ONLY_ACTIVE_ARCH=NO GCC_INSTRUMENT_PROGRAM_FLOW_ARCS=YES GCC_GENERATE_TEST_COVERAGE_FILES=YES 10 | 11 | - xcodebuild -project libPhoneNumber.xcodeproj -scheme libPhoneNumberGeocoding -sdk iphonesimulator ONLY_ACTIVE_ARCH=NO GCC_INSTRUMENT_PROGRAM_FLOW_ARCS=YES build-for-testing 12 | - xctool -project libPhoneNumber.xcodeproj -scheme libPhoneNumberGeocoding run-tests -sdk iphonesimulator ONLY_ACTIVE_ARCH=NO GCC_INSTRUMENT_PROGRAM_FLOW_ARCS=YES GCC_GENERATE_TEST_COVERAGE_FILES=YES 13 | 14 | - xcodebuild -project libPhoneNumber.xcodeproj -scheme libPhoneNumberShortNumber -sdk iphonesimulator ONLY_ACTIVE_ARCH=NO GCC_INSTRUMENT_PROGRAM_FLOW_ARCS=YES build-for-testing 15 | - xctool -project libPhoneNumber.xcodeproj -scheme libPhoneNumberShortNumber run-tests -sdk iphonesimulator ONLY_ACTIVE_ARCH=NO GCC_INSTRUMENT_PROGRAM_FLOW_ARCS=YES GCC_GENERATE_TEST_COVERAGE_FILES=YES 16 | 17 | after_success: slather 18 | -------------------------------------------------------------------------------- /libPhoneNumber-GeocodingParser/libPhoneNumber-GeocodingParser/ArgumentParser.m: -------------------------------------------------------------------------------- 1 | // 2 | // ArgumentParser.m 3 | // libPhoneNumber-GeocodingParser 4 | // 5 | // Created by Kris Kline on 11/12/25. 6 | // Copyright © 2025 Rastaar Haghi. All rights reserved. 7 | // 8 | 9 | #import "ArgumentParser.h" 10 | 11 | #import "NSString+Extensions.h" 12 | 13 | ArgResult *parseArguments(int argc, const char * argv[]) { 14 | NSString *dir = nil; 15 | NSString *ver = nil; 16 | for(int i=1; idirectory = dir; 38 | result->version = ver; 39 | return result; 40 | } 41 | -------------------------------------------------------------------------------- /libPhoneNumberShortNumber.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "libPhoneNumberShortNumber" 3 | s.version = "1.3.1" 4 | s.summary = "Short Number Support for libPhoneNumber-iOS" 5 | s.description = "libPhoneNumberShortNumber for iOS. iOS library for implementing libPhoneNumber features on short numbers." 6 | s.homepage = "https://github.com/iziz/libPhoneNumber-iOS.git" 7 | s.license = 'Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)' 8 | s.authors = { 9 | "rastaarh" => "rastaar@google.com", 10 | "paween" => "paween@google.com", 11 | "Kris Kline" => "kris.kline@oracle.com" 12 | } 13 | 14 | s.source = { 15 | :git => "https://github.com/iziz/libPhoneNumber-iOS.git", 16 | :tag => s.version.to_s 17 | } 18 | 19 | s.libraries = 'z' 20 | 21 | s.ios.deployment_target = "12.0" 22 | s.osx.deployment_target = "10.13" 23 | s.watchos.deployment_target = "4.0" 24 | s.tvos.deployment_target = "12.0" 25 | 26 | s.requires_arc = true 27 | 28 | s.private_header_files = 'libPhoneNumberShortNumberInternal/**/*.h' 29 | 30 | s.dependency 'libPhoneNumber-iOS', '~> 1.3.0' 31 | 32 | s.source_files = [ 33 | 'libPhoneNumberShortNumberInternal/**/*.{h,m}', 34 | 'libPhoneNumberShortNumber/**/*.{h,m}', 35 | ] 36 | end 37 | -------------------------------------------------------------------------------- /libPhoneNumberGeocoding.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "libPhoneNumberGeocoding" 3 | s.version = "1.3.1" 4 | s.summary = "Geocoding Features for libPhoneNumber-iOS" 5 | s.description = "libPhoneNumberGeocoding for iOS. iOS library for gathering region descriptions for any phone number. This library stores geocoding metadata on disk space." 6 | s.homepage = "https://github.com/iziz/libPhoneNumber-iOS.git" 7 | s.license = 'Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)' 8 | s.authors = { 9 | "rastaarh" => "rastaar@google.com", 10 | "paween" => "paween@google.com", 11 | "aalexli" => "aalexli@google.com", 12 | "Kris Kline" => "kris.kline@oracle.com" 13 | } 14 | 15 | s.source = { 16 | :git => "https://github.com/iziz/libPhoneNumber-iOS.git", 17 | :tag => s.version.to_s 18 | } 19 | 20 | s.libraries = 'sqlite3' 21 | 22 | s.ios.deployment_target = "12.0" 23 | s.osx.deployment_target = "10.13" 24 | s.watchos.deployment_target = "4.0" 25 | s.tvos.deployment_target = "12.0" 26 | 27 | s.requires_arc = true 28 | 29 | s.resources = "libPhoneNumberGeocodingMetaData/*.bundle" 30 | 31 | s.dependency 'libPhoneNumber-iOS', '~> 1.3.0' 32 | 33 | s.source_files = [ 34 | 'libPhoneNumberGeocoding/**/*.{h,m}', 35 | ] 36 | end 37 | -------------------------------------------------------------------------------- /libPhoneNumber-Demo/libPhoneNumber-Demo/FormatterView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FormatterView.swift 3 | // libPhoneNumber-Demo 4 | // 5 | // Created by Rastaar Haghi on 7/17/20. 6 | // Copyright © 2020 Google LLC. All rights reserved. 7 | // 8 | 9 | import SwiftUI 10 | 11 | #if canImport(libPhoneNumber) 12 | import libPhoneNumber 13 | #elseif canImport(libPhoneNumber_iOS) 14 | import libPhoneNumber_iOS 15 | #endif 16 | 17 | struct FormatterView: View { 18 | @State var phoneNumber: String = "" 19 | let formatter: NBAsYouTypeFormatter = NBAsYouTypeFormatter(regionCode: Locale.current.regionCode) 20 | 21 | var body: some View { 22 | VStack { 23 | Form { 24 | Section(header: Text("Phone Number")) { 25 | TextField("Ex: 19091234567", text: $phoneNumber) 26 | .textFieldStyle(RoundedBorderTextFieldStyle()) 27 | .font(.largeTitle) 28 | .padding() 29 | } 30 | .padding() 31 | Section(header: Text("Formatted Phone Number")) { 32 | Text(formatPhoneNumber(phoneNumber: phoneNumber)) 33 | } 34 | } 35 | } 36 | .navigationBarTitle("As-You-Type Formatter") 37 | .lineLimit(2) 38 | .multilineTextAlignment(.center) 39 | } 40 | 41 | func formatPhoneNumber(phoneNumber: String) -> String { 42 | formatter.clear() 43 | return formatter.inputString(phoneNumber) 44 | } 45 | } 46 | 47 | struct FormatterView_Previews: PreviewProvider { 48 | static var previews: some View { 49 | FormatterView() 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Authors ordered by first contribution. 2 | 3 | SungMin Ha (ishtar aka iziz, zen.isis@gmail.com) 4 | Hyuk Hur (hyukhur@gmail.com) 5 | 6 | Cliff Rowley (cliffrowley@gmail.com) 7 | - Missing return value in formatInOriginalFormat:regionCallingFrom:error 8 | - Compatibility with iOS versions prior to 6.x 9 | 10 | Aaron Wojnowski (aaronwojnowski@gmail.com) 11 | - Remove extra r from the countryCodeFromRegionCode method name. 12 | 13 | stonyw 14 | - Handle char @ when normalize any strings, eg email 15 | 16 | Yosi Taguri (http://ahhhpah.com) 17 | - Failed tests are down to 18 for [ AsYouTypeFormatter ] 18 | 19 | Nils Hayat (http://nilsou.com) 20 | - Help to update cocoapods 21 | - Added -[NBPhoneNumberUtil parseWithPhoneCarrierRegion:error:] to parse a number using the phone's carrier country code 22 | 23 | Frane Bandov frane (frane@offbyte.com, http://frane.offbyte.com) 24 | - Refactor to reduce memory consumption 25 | (Export NSArray category into separate files, 26 | Optimize constants, 27 | Cache regex patterns to reduce memory and CPU consumption, 28 | Make countyCodeByCarrier method publically available, 29 | Add libPhoneNumber.{h,m} to podspec) 30 | 31 | Cliff Ingham (inghamn@bloomington.in.gov, http://bloomington.in.gov/) 32 | - Added explicit type casts to avoid warnings when building for 64 bit iOS... 33 | 34 | rhoiberg 35 | - Allow NBPhoneNumberUtil to be created using a specified bundle. 36 | 37 | Google Inc. 38 | - Add some digits mapping. 39 | - Reduce Library Size by compiling data directly into binary instead of generating a class per region. 40 | -------------------------------------------------------------------------------- /libPhoneNumber-Demo/libPhoneNumber-Demo/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // libPhoneNumber-Demo 4 | // 5 | // Created by Rastaar Haghi on 7/17/20. 6 | // Copyright © 2020 Google LLC. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | func application( 14 | _ application: UIApplication, 15 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 16 | ) -> Bool { 17 | // Override point for customization after application launch. 18 | return true 19 | } 20 | 21 | // MARK: UISceneSession Lifecycle 22 | 23 | func application( 24 | _ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, 25 | options: UIScene.ConnectionOptions 26 | ) -> UISceneConfiguration { 27 | // Called when a new scene session is being created. 28 | // Use this method to select a configuration to create the new scene with. 29 | return UISceneConfiguration( 30 | name: "Default Configuration", sessionRole: connectingSceneSession.role) 31 | } 32 | 33 | func application( 34 | _ application: UIApplication, didDiscardSceneSessions sceneSessions: Set 35 | ) { 36 | // Called when the user discards a scene session. 37 | // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. 38 | // Use this method to release any resources that were specific to the discarded scenes, as they will not return. 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /libPhoneNumber/NBRegularExpressionCache.m: -------------------------------------------------------------------------------- 1 | // 2 | // NBRegularExpressionCache.m 3 | // libPhoneNumber 4 | // 5 | // Created by Paween Itthipalkul on 11/29/17. 6 | // Copyright © 2017 Google LLC. All rights reserved. 7 | // 8 | 9 | #import "NBRegularExpressionCache.h" 10 | 11 | @interface NBRegularExpressionCache() 12 | 13 | @property (nonatomic, strong) NSCache *cache; 14 | 15 | @end 16 | 17 | @implementation NBRegularExpressionCache 18 | 19 | + (instancetype)sharedInstance { 20 | static NBRegularExpressionCache *instance; 21 | static dispatch_once_t token; 22 | dispatch_once(&token, ^{ 23 | instance = [[NBRegularExpressionCache alloc] init]; 24 | }); 25 | 26 | return instance; 27 | } 28 | 29 | - (instancetype)init { 30 | self = [super init]; 31 | if (self != nil) { 32 | _cache = [[NSCache alloc] init]; 33 | } 34 | 35 | return self; 36 | } 37 | 38 | - (NSRegularExpression *)regularExpressionForPattern:(NSString *)pattern error:(NSError **)error { 39 | @synchronized(self) { 40 | NSRegularExpression *cachedObject = [self.cache objectForKey:pattern]; 41 | if (cachedObject != nil) { 42 | return cachedObject; 43 | } 44 | 45 | NSError *regExError = nil; 46 | NSRegularExpression *regEx = [[NSRegularExpression alloc] initWithPattern:pattern 47 | options:kNilOptions 48 | error:®ExError]; 49 | if (regEx == nil) { 50 | if (error != NULL) { 51 | *error = regExError; 52 | } 53 | return nil; 54 | } 55 | 56 | [self.cache setObject:regEx forKey:pattern]; 57 | 58 | return regEx; 59 | } 60 | } 61 | 62 | @end 63 | -------------------------------------------------------------------------------- /libPhoneNumber-iOS.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "libPhoneNumber-iOS" 3 | s.version = "1.3.1" 4 | s.summary = "iOS library for parsing, formatting, storing and validating international phone numbers from libphonenumber library." 5 | s.description = "libPhoneNumber for iOS. iOS library for parsing, formatting, storing and validating international phone numbers from libphonenumber library." 6 | s.homepage = "https://github.com/iziz/libPhoneNumber-iOS.git" 7 | s.license = 'Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)' 8 | s.authors = { 9 | "iziz" => "zen.isis@gmail.com", 10 | "hyukhur" => "hyukhur@gmail.com", 11 | "Kris Kline" => "kris.kline@oracle.com" 12 | } 13 | 14 | s.source = { 15 | :git => "https://github.com/iziz/libPhoneNumber-iOS.git", 16 | :tag => s.version.to_s 17 | } 18 | 19 | s.libraries = 'z' 20 | s.ios.framework = 'Contacts' 21 | 22 | s.ios.deployment_target = "12.0" 23 | s.osx.deployment_target = "10.13" 24 | s.watchos.deployment_target = "4.0" 25 | s.tvos.deployment_target = "12.0" 26 | 27 | s.requires_arc = true 28 | 29 | s.private_header_files = [ 30 | 'libPhoneNumberInternal/**/*.h', 31 | ] 32 | 33 | s.source_files = [ 34 | 'libPhoneNumberInternal/**/*.{h,m}', 35 | 'libPhoneNumber/**/*.{h,m}', 36 | ] 37 | 38 | s.resource_bundles = { 39 | 'libPhoneNumber-iOS' => ['libPhoneNumber/PrivacyInfo.xcprivacy'] 40 | } 41 | 42 | end 43 | -------------------------------------------------------------------------------- /libPhoneNumberShortNumberTests/NBShortNumberTestHelper.m: -------------------------------------------------------------------------------- 1 | // 2 | // NBShortNumberTestHelper.m 3 | // libPhoneNumberShortNumber 4 | // 5 | // Created by Paween Itthipalkul on 12/1/17. 6 | // Copyright © 2017 Google LLC. All rights reserved. 7 | // 8 | #import "NBShortNumberTestHelper.h" 9 | #import "NBPhoneMetaData.h" 10 | #import "NBPhoneNumberDesc.h" 11 | #import "NBShortNumberMetadataHelper.h" 12 | #import "NBShortNumberUtil.h" 13 | 14 | @implementation NBShortNumberTestHelper { 15 | NBShortNumberMetadataHelper *_helper; 16 | } 17 | 18 | - (instancetype)init { 19 | self = [super init]; 20 | if (self != nil) { 21 | _helper = [[NBShortNumberMetadataHelper alloc] init]; 22 | } 23 | return self; 24 | } 25 | 26 | - (NSString *)exampleShortNumberForCost:(NBEShortNumberCost)cost regionCode:(NSString *)regionCode { 27 | NBPhoneMetaData *metadata = [_helper shortNumberMetadataForRegion:regionCode]; 28 | if (metadata == nil) { 29 | return @""; 30 | } 31 | 32 | NBPhoneNumberDesc *desc = nil; 33 | switch (cost) { 34 | case NBEShortNumberCostTollFree: 35 | desc = metadata.tollFree; 36 | break; 37 | case NBEShortNumberCostPremiumRate: 38 | desc = metadata.premiumRate; 39 | break; 40 | case NBEShortNumberCostStandardRate: 41 | desc = metadata.standardRate; 42 | break; 43 | case NBEShortNumberCostUnknown: 44 | // UNKNOWN_COST numbers are computed by the process of elimination from the other cost 45 | // categories. 46 | break; 47 | } 48 | 49 | return desc.exampleNumber ?: @""; 50 | } 51 | 52 | - (NSString *)exampleShortNumberWithRegionCode:(NSString *)regionCode { 53 | NBPhoneMetaData *metadata = [_helper shortNumberMetadataForRegion:regionCode]; 54 | return metadata.shortCode.exampleNumber ?: @""; 55 | } 56 | 57 | @end 58 | -------------------------------------------------------------------------------- /libPhoneNumber-GeocodingParser/README.md: -------------------------------------------------------------------------------- 1 | # **libPhoneNumber-iOS Geocoding File Parser** 2 | 3 | An Objective-C program that converts Google's libPhoneNumber geocoding information into SQLite databases that 4 | can be used in the libPhoneNumber-iOS library for geocoding functionality. This program downloads Google's 5 | libPhoneNumber repository and iterates through each language folder of geocoding metadata and creates an 6 | individual SQLite database file for each language. The program creates individual tables for each country code of 7 | geocoding region data provided for each language. 8 | 9 | ## Usage 10 | 11 | This program has 1 required input arguments for ```main.m```: 12 | 13 | 1) The local system directory path to the desired destination to save the SQLite database files. 14 | Example: /Users/JohnDoe/Documents 15 | 16 | ## Updating libPhoneNumber-iOS GeocodingMetaData.bundle 17 | 18 | libPhoneNumber's geocoding metadata files are periodically updated by contributors. Please fetch the most recently 19 | updated geocoding data files by using this program and replace the current database files in 20 | GeocodingMetaData.bundle, found in the [libPhoneNumberGeocoding target](https://github.com/iziz/libPhoneNumber-iOS/tree/master/libPhoneNumberGeocodingMetaData). 21 | 22 | Please contribute to libPhoneNumber-iOS library by creating a pull request to replace outdated databases with the 23 | up-to-date SQLite databases produced by this program. 24 | 25 | 26 | ##### Visit [libphonenumber](https://github.com/google/libphonenumber) for more information 27 | 28 | ### Update Steps 29 | 1. Open the libPhoneNumber-GeocodingParser project in Xcode 30 | 2. Edit the libPhoneNumber-GeocodingParser Scheme 31 | 3. In the `Run` section go to the `Arguments` tab 32 | 4. Edit the version argument to be the desired version number 33 | 5. Add an argument specifying the output directory (ex. `/Users/john.doe/geocoding`) 34 | 6. Run the libPhoneNumber-GeocodingParser program in Xcode (`Cmd+R`) on your machine 35 | 7. Wait a few minutes for the program to complete 36 | 8. Copy the `*.db` files from your specified output directory to `libPhoneNumberGeocodingMetaData/GeocodingMetaData.bundle` 37 | -------------------------------------------------------------------------------- /libPhoneNumber-Demo/libPhoneNumber-Demo/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 | $(MARKETING_VERSION) 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | LSRequiresIPhoneOS 22 | 23 | UIApplicationSceneManifest 24 | 25 | UIApplicationSupportsMultipleScenes 26 | 27 | UISceneConfigurations 28 | 29 | UIWindowSceneSessionRoleApplication 30 | 31 | 32 | UISceneConfigurationName 33 | Default Configuration 34 | UISceneDelegateClassName 35 | $(PRODUCT_MODULE_NAME).SceneDelegate 36 | 37 | 38 | 39 | 40 | UILaunchStoryboardName 41 | LaunchScreen 42 | UIRequiredDeviceCapabilities 43 | 44 | armv7 45 | 46 | UISupportedInterfaceOrientations 47 | 48 | UIInterfaceOrientationPortrait 49 | UIInterfaceOrientationLandscapeLeft 50 | UIInterfaceOrientationLandscapeRight 51 | 52 | UISupportedInterfaceOrientations~ipad 53 | 54 | UIInterfaceOrientationPortrait 55 | UIInterfaceOrientationPortraitUpsideDown 56 | UIInterfaceOrientationLandscapeLeft 57 | UIInterfaceOrientationLandscapeRight 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /libPhoneNumberTestsCommon/NSBundle+Extensions.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSBundle+Extensions.m 3 | // libPhoneNumber 4 | // 5 | // Created by Kris Kline on 11/5/25. 6 | // 7 | 8 | #import "NSBundle+Extensions.h" 9 | 10 | @implementation NSBundle(PathForFirst) 11 | 12 | /** 13 | * Recursive method for searching through all the bundles in the array for the specified file, but also seach all child bundles of each bundle in the array, until the file is found 14 | * 15 | * - parameter name: The name of the file to look for: . 16 | * - parameter type: The type of the file to look for: . 17 | * 18 | * - returns The path to the found resource. Nil if the specified resource couldn't be found in any bundles 19 | */ 20 | +(nullable NSString*)pathInBundles:(NSArray*)bundles forFirstResourceNamed:(nonnull NSString*)name ofType:(nullable NSString*)type { 21 | for(NSBundle* b in bundles) { 22 | NSString* path = [b pathForResource:name ofType:type]; 23 | if(path != NULL) { 24 | return path; 25 | } 26 | 27 | path = [NSBundle pathInBundles:[b childBundles] forFirstResourceNamed:name ofType:type]; 28 | if(path != NULL) { 29 | return path; 30 | } 31 | } 32 | 33 | return NULL; 34 | } 35 | 36 | +(nullable NSString*)pathForFirstResourceNamed:(nonnull NSString*)name ofType:(nullable NSString*)type { 37 | return [self pathInBundles:[NSBundle allBundles] forFirstResourceNamed:name ofType:type]; 38 | } 39 | 40 | - (nullable NSArray *)childBundles { 41 | NSMutableArray *foundBundles = [NSMutableArray array]; 42 | 43 | // Get the path of the parent bundle 44 | NSString *parentBundlePath = self.bundlePath; 45 | 46 | // Get all files and directories in the bundle's root directory 47 | NSArray *contents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:parentBundlePath error:nil]; 48 | 49 | for (NSString *item in contents) { 50 | // Check if the item has a .bundle extension 51 | if ([item.pathExtension isEqualToString:@"bundle"]) { 52 | NSString *bundlePath = [parentBundlePath stringByAppendingPathComponent:item]; 53 | NSBundle *bundle = [NSBundle bundleWithPath:bundlePath]; 54 | if (bundle) { 55 | [foundBundles addObject:bundle]; 56 | } 57 | } 58 | } 59 | 60 | if(foundBundles.count>0) { 61 | return foundBundles; 62 | } 63 | 64 | return NULL; 65 | } 66 | 67 | @end 68 | -------------------------------------------------------------------------------- /libPhoneNumberGeocoding/NBGeocoderMetaDataHelper.h: -------------------------------------------------------------------------------- 1 | // 2 | // NBGeocoderMetaDataHelper.h 3 | // libPhoneNumberiOS 4 | // 5 | // Created by Rastaar Haghi on 6/12/20. 6 | // Copyright © 2020 Google LLC. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class NBPhoneNumber; 12 | 13 | NS_ASSUME_NONNULL_BEGIN 14 | 15 | @interface NBGeocoderMetaDataHelper : NSObject 16 | 17 | /** 18 | * Initializer method that creates a SQLite3 connection to the correct database (based on 19 | * the language passed in as a parameter), which is stored in the resource bundle of this project. 20 | * This method opens the database for viewing if possible and prepares a query statement with 21 | * the country code parameter 22 | * 23 | * @param countryCode a valid country code for which we want to set up a prepared statement to 24 | * access a SQLite table with that country code. 25 | * @param languageCode the language code for which the description should be written 26 | * @return a NBGeocoderMetaDataHelper instance variable with an initial country code and language 27 | * code set to the parameters, countryCode and languageCode, or nil if the NSObject couldn't be 28 | * initialized or there was no database URL found for the provided language code. 29 | */ 30 | - (instancetype)initWithCountryCode:(NSNumber *)countryCode 31 | withLanguage:(NSString *)languageCode 32 | withBundle:(NSBundle *)bundle; 33 | 34 | - (instancetype)initWithCountryCode:(NSNumber *)countryCode withLanguage:(NSString *)languageCode; 35 | 36 | /** 37 | * Returns a text description for the given phone number. The description will be based on the 38 | * country code and national number attributes of the NBPhoneNumber parameter. If no result was 39 | * found from querying the geocoding databases, this method will return nil. 40 | * 41 | * This method assumes the validity of the NBPhoneNumber object passed in that it contains a 42 | * countryCode and nationalNumber attribute. This method prepares a select statement to search 43 | * through a SQLite database for the most accurate text description for the phone number parameter. 44 | * 45 | * @param phoneNumber a valid phone number for which we want to get a text description 46 | * @return a text description for the given language code for the given phone number, or nil if 47 | * there were no search results found for the given phone number. 48 | */ 49 | - (NSString * _Nullable)searchPhoneNumber:(NBPhoneNumber *)phoneNumber; 50 | 51 | @end 52 | 53 | NS_ASSUME_NONNULL_END 54 | -------------------------------------------------------------------------------- /libPhoneNumber/NBPhoneMetaData.h: -------------------------------------------------------------------------------- 1 | // 2 | // M2PhoneMetaData.h 3 | // libPhoneNumber 4 | // 5 | // 6 | 7 | #import 8 | 9 | @class NBPhoneNumberDesc, NBNumberFormat; 10 | 11 | @interface NBPhoneMetaData : NSObject 12 | 13 | // from phonemetadata.proto 14 | /* 1 */ @property(nonatomic, strong) NBPhoneNumberDesc *generalDesc; 15 | /* 2 */ @property(nonatomic, strong) NBPhoneNumberDesc *fixedLine; 16 | /* 3 */ @property(nonatomic, strong) NBPhoneNumberDesc *mobile; 17 | /* 4 */ @property(nonatomic, strong) NBPhoneNumberDesc *tollFree; 18 | /* 5 */ @property(nonatomic, strong) NBPhoneNumberDesc *premiumRate; 19 | /* 6 */ @property(nonatomic, strong) NBPhoneNumberDesc *sharedCost; 20 | /* 7 */ @property(nonatomic, strong) NBPhoneNumberDesc *personalNumber; 21 | /* 8 */ @property(nonatomic, strong) NBPhoneNumberDesc *voip; 22 | /* 21 */ @property(nonatomic, strong) NBPhoneNumberDesc *pager; 23 | /* 25 */ @property(nonatomic, strong) NBPhoneNumberDesc *uan; 24 | /* 27 */ @property(nonatomic, strong) NBPhoneNumberDesc *emergency; 25 | /* 28 */ @property(nonatomic, strong) NBPhoneNumberDesc *voicemail; 26 | /* 24 */ @property(nonatomic, strong) NBPhoneNumberDesc *noInternationalDialling; 27 | /* 9 */ @property(nonatomic, strong) NSString *codeID; 28 | /* 10 */ @property(nonatomic, strong) NSNumber *countryCode; 29 | /* 11 */ @property(nonatomic, strong) NSString *internationalPrefix; 30 | /* 17 */ @property(nonatomic, strong) NSString *preferredInternationalPrefix; 31 | /* 12 */ @property(nonatomic, strong) NSString *nationalPrefix; 32 | /* 13 */ @property(nonatomic, strong) NSString *preferredExtnPrefix; 33 | /* 15 */ @property(nonatomic, strong) NSString *nationalPrefixForParsing; 34 | /* 16 */ @property(nonatomic, strong) NSString *nationalPrefixTransformRule; 35 | /* 18 */ @property(nonatomic, assign) BOOL sameMobileAndFixedLinePattern; 36 | /* 19 */ @property(nonatomic, strong) NSArray *numberFormats; 37 | /* 20 */ @property(nonatomic, strong) NSArray *intlNumberFormats; 38 | /* 22 */ @property(nonatomic, assign) BOOL mainCountryForCode; 39 | /* 23 */ @property(nonatomic, strong) NSString *leadingDigits; 40 | /* 26 */ @property(nonatomic, assign) BOOL leadingZeroPossible; 41 | 42 | /* 29 */ @property(nonatomic, strong) NBPhoneNumberDesc *shortCode; 43 | /* 30 */ @property(nonatomic, strong) NBPhoneNumberDesc *standardRate; 44 | /* 31 */ @property(nonatomic, strong) NBPhoneNumberDesc *carrierSpecific; 45 | /* 33 */ @property(nonatomic, strong) NBPhoneNumberDesc *smsServices; 46 | 47 | - (instancetype)initWithEntry:(NSArray *)entry; 48 | @end 49 | -------------------------------------------------------------------------------- /libPhoneNumber-Demo/libPhoneNumber-Demo/GeocodingSearchView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GeocodingSearchView.swift 3 | // libPhoneNumber-Demo 4 | // 5 | // Created by Rastaar Haghi on 7/17/20. 6 | // Copyright © 2020 Google LLC. All rights reserved. 7 | // 8 | 9 | import SwiftUI 10 | import libPhoneNumberGeocoding 11 | 12 | #if canImport(libPhoneNumber) 13 | import libPhoneNumber 14 | #elseif canImport(libPhoneNumber_iOS) 15 | import libPhoneNumber_iOS 16 | #endif 17 | 18 | struct GeocodingSearchView: View { 19 | @State private var localeSelection = 0 20 | @State private var phoneNumber: String = "" 21 | @State private var regionDescription: String = "" 22 | 23 | private let geocoder: NBPhoneNumberOfflineGeocoder = NBPhoneNumberOfflineGeocoder() 24 | 25 | var body: some View { 26 | VStack { 27 | Form { 28 | Section(header: Text("Locale Options")) { 29 | Picker("Locale Options", selection: $localeSelection) { 30 | ForEach(locales.indices, id: \.self) { index in 31 | Text(locales[index].language) 32 | .tag(index) 33 | } 34 | } 35 | } 36 | Section(header: Text("Phone Number")) { 37 | TextField("Ex: 19098611234", text: $phoneNumber) 38 | .textFieldStyle(RoundedBorderTextFieldStyle()) 39 | .padding() 40 | } 41 | Button( 42 | action: { 43 | self.regionDescription = self.searchPhoneNumber() 44 | }, 45 | label: { 46 | Text("Search for Region Description") 47 | .multilineTextAlignment(.center) 48 | }) 49 | } 50 | Text(regionDescription) 51 | .bold() 52 | } 53 | .navigationBarTitle(Text("Search for Region Description")) 54 | } 55 | } 56 | 57 | extension GeocodingSearchView { 58 | func searchPhoneNumber() -> String { 59 | do { 60 | let parsedPhoneNumber: NBPhoneNumber = 61 | try NBPhoneNumberUtil.sharedInstance().parse(phoneNumber, defaultRegion: Locale.current.regionCode!) 62 | if localeSelection == 0 { 63 | return geocoder.description(for: parsedPhoneNumber) ?? "Unknown Region" 64 | } else { 65 | return geocoder.description( 66 | for: parsedPhoneNumber, 67 | withLanguageCode: locales[localeSelection].localeCode) 68 | ?? "Unknown Region" 69 | } 70 | } catch { 71 | print(error) 72 | return "Error parsing phone number." 73 | } 74 | } 75 | } 76 | 77 | struct GeocodingSearchView_Previews: PreviewProvider { 78 | static var previews: some View { 79 | GeocodingSearchView() 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /libPhoneNumber-Demo/libPhoneNumber-Demo/Locales.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Locales.swift 3 | // libPhoneNumber-Demo 4 | // 5 | // Created by Rastaar Haghi on 7/20/20. 6 | // Copyright © 2020 Google LLC. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import libPhoneNumberShortNumber 11 | 12 | struct LocaleInfo { 13 | let localeCode: String 14 | let language: String 15 | } 16 | 17 | // These are all of the languages supported by libPhoneNumber. 18 | let locales: [LocaleInfo] = [ 19 | LocaleInfo(localeCode: "Default Device Language", language: "Default Device Language"), 20 | LocaleInfo(localeCode: "ar", language: "Arabic"), 21 | LocaleInfo(localeCode: "be", language: "Belarusian"), 22 | LocaleInfo(localeCode: "bg", language: "Bulgarian"), 23 | LocaleInfo(localeCode: "bs", language: "Bosnian"), 24 | LocaleInfo(localeCode: "de", language: "German"), 25 | LocaleInfo(localeCode: "el", language: "Greek"), 26 | LocaleInfo(localeCode: "en", language: "English"), 27 | LocaleInfo(localeCode: "es", language: "Spanish"), 28 | LocaleInfo(localeCode: "fa", language: "Persian"), 29 | LocaleInfo(localeCode: "fi", language: "Finnish"), 30 | LocaleInfo(localeCode: "fr", language: "French"), 31 | LocaleInfo(localeCode: "hr", language: "Croatian"), 32 | LocaleInfo(localeCode: "hu", language: "Hungarian"), 33 | LocaleInfo(localeCode: "hy", language: "Armenian"), 34 | LocaleInfo(localeCode: "id", language: "Indonesian"), 35 | LocaleInfo(localeCode: "it", language: "Italian"), 36 | LocaleInfo(localeCode: "iw", language: "Hebrew"), 37 | LocaleInfo(localeCode: "ja", language: "Japanese"), 38 | LocaleInfo(localeCode: "ko", language: "Korean"), 39 | LocaleInfo(localeCode: "nl", language: "Dutch"), 40 | LocaleInfo(localeCode: "pl", language: "Polish"), 41 | LocaleInfo(localeCode: "pt", language: "Portuguese"), 42 | LocaleInfo(localeCode: "ro", language: "Romanian"), 43 | LocaleInfo(localeCode: "ru", language: "Russian"), 44 | LocaleInfo(localeCode: "sq", language: "Albanian"), 45 | LocaleInfo(localeCode: "sr", language: "Serbian"), 46 | LocaleInfo(localeCode: "sv", language: "Swedish"), 47 | LocaleInfo(localeCode: "th", language: "Thai"), 48 | LocaleInfo(localeCode: "tr", language: "Turkish"), 49 | LocaleInfo(localeCode: "uk", language: "Ukrainian"), 50 | LocaleInfo(localeCode: "vi", language: "Vietnamese"), 51 | LocaleInfo(localeCode: "zh", language: "Chinese"), 52 | LocaleInfo(localeCode: "zh_Hant", language: "Chinese (Traditional)"), 53 | ] 54 | 55 | // These are all of the formatting options supported by libPhoneNumber. 56 | let formatOptions = [ 57 | "Select a phone number format", "E164", "INTERNATIONAL", "NATIONAL", "RFC3966", 58 | ] 59 | -------------------------------------------------------------------------------- /libPhoneNumber-Demo/libPhoneNumber-Demo/SceneDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SceneDelegate.swift 3 | // libPhoneNumber-Demo 4 | // 5 | // Created by Rastaar Haghi on 7/17/20. 6 | // Copyright © 2020 Google LLC. All rights reserved. 7 | // 8 | 9 | import SwiftUI 10 | import UIKit 11 | 12 | class SceneDelegate: UIResponder, UIWindowSceneDelegate { 13 | var window: UIWindow? 14 | 15 | func scene( 16 | _ scene: UIScene, willConnectTo session: UISceneSession, 17 | options connectionOptions: UIScene.ConnectionOptions 18 | ) { 19 | // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. 20 | // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. 21 | // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). 22 | 23 | // Create the SwiftUI view that provides the window contents. 24 | let contentView = MenuView() 25 | 26 | // Use a UIHostingController as window root view controller. 27 | if let windowScene = scene as? UIWindowScene { 28 | let window = UIWindow(windowScene: windowScene) 29 | window.rootViewController = UIHostingController(rootView: contentView) 30 | self.window = window 31 | window.makeKeyAndVisible() 32 | } 33 | } 34 | 35 | func sceneDidDisconnect(_ scene: UIScene) { 36 | // Called as the scene is being released by the system. 37 | // This occurs shortly after the scene enters the background, or when its session is discarded. 38 | // Release any resources associated with this scene that can be re-created the next time the scene connects. 39 | // The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead). 40 | } 41 | 42 | func sceneDidBecomeActive(_ scene: UIScene) { 43 | // Called when the scene has moved from an inactive state to an active state. 44 | // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. 45 | } 46 | 47 | func sceneWillResignActive(_ scene: UIScene) { 48 | // Called when the scene will move from an active state to an inactive state. 49 | // This may occur due to temporary interruptions (ex. an incoming phone call). 50 | } 51 | 52 | func sceneWillEnterForeground(_ scene: UIScene) { 53 | // Called as the scene transitions from the background to the foreground. 54 | // Use this method to undo the changes made on entering the background. 55 | } 56 | 57 | func sceneDidEnterBackground(_ scene: UIScene) { 58 | // Called as the scene transitions from the foreground to the background. 59 | // Use this method to save data, release shared resources, and store enough scene-specific state information 60 | // to restore the scene back to its current state. 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /libPhoneNumber-GeocodingParser/libPhoneNumber-GeocodingParser/NBGeocoderMetadataParser.m: -------------------------------------------------------------------------------- 1 | // 2 | // NBGeocoderMetadataParser.m 3 | // libPhoneNumber-GeocodingParser 4 | // 5 | // Created by Rastaar Haghi on 7/1/20. 6 | // Copyright © 2020 Google LLC. All rights reserved. 7 | // 8 | 9 | #import "NBGeocoderMetadataParser.h" 10 | 11 | @interface NBGeocoderMetadataParser() 12 | 13 | @property (nonatomic, strong) NSURL* destinationPath; 14 | 15 | @end 16 | 17 | @implementation NBGeocoderMetadataParser 18 | 19 | - (instancetype)initWithDestinationPath:(NSURL *)destinationPath { 20 | self = [super init]; 21 | if (self != nil) { 22 | self.destinationPath = destinationPath; 23 | } 24 | return self; 25 | } 26 | 27 | - (void)convertFileToSQLiteDatabase:(NSString *)completeTextFilePath 28 | withFileName:(NSString *)textFileName 29 | withLanguage:(NSString *)languageCode 30 | loggingIndent:(NSString *)indent { 31 | NSString *fileContentString = [[NSString alloc] initWithContentsOfFile:completeTextFilePath 32 | encoding:NSUTF8StringEncoding 33 | error:nil]; 34 | NSArray *countryCodes = [textFileName componentsSeparatedByString:@"."]; 35 | 36 | NSString *countryCode = countryCodes[0]; 37 | NSLog(@"%@Creating a SQL table for the country code: %@, in the language: %@", (indent!=nil) ? indent : @"", countryCode, languageCode); 38 | NBSQLiteDatabaseConnection *databaseConnection = 39 | [[NBSQLiteDatabaseConnection alloc] initWithCountryCode:countryCode 40 | withLanguage:languageCode 41 | withDestinationPath:self.destinationPath]; 42 | 43 | // Split into phone number prefix and region description. 44 | NSCharacterSet *separator = [NSCharacterSet newlineCharacterSet]; 45 | NSArray *components = [fileContentString componentsSeparatedByCharactersInSet:separator]; 46 | NSArray *keyValuePair; 47 | 48 | NSString *key, *value; 49 | BOOL indexNeededFlag = YES; 50 | for (NSString *str in components) { 51 | @autoreleasepool { 52 | // Skip entry if invalid, malformatted, or comment. 53 | if (([str length] > 0) && ([str characterAtIndex:0] == '#')) { 54 | continue; 55 | } 56 | keyValuePair = [str componentsSeparatedByString:@"|"]; 57 | if ([keyValuePair count] != 2) { 58 | continue; 59 | } 60 | key = [keyValuePair[0] 61 | stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; 62 | value = [keyValuePair[1] 63 | stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; 64 | [databaseConnection addEntryToDB:key withDescription:value withCountryCode:countryCode]; 65 | indexNeededFlag = NO; 66 | } 67 | } 68 | } 69 | 70 | @end 71 | -------------------------------------------------------------------------------- /libPhoneNumber-Demo/libPhoneNumber-Demo/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /libPhoneNumberShortNumber/README.md: -------------------------------------------------------------------------------- 1 | 2 | # **libPhoneNumberShortNumber for iOS** 3 | 4 | - NBShortNumberUtil 5 | 6 | > ARC only 7 | 8 | ## Update Log 9 | [https://github.com/iziz/libPhoneNumber-iOS/wiki/Update-Log](https://github.com/iziz/libPhoneNumber-iOS/wiki/Update-Log) 10 | 11 | 12 | ## Install 13 | 14 | The libPhoneNumberShortNumber pod requires the libPhoneNumber-iOS framework in order to build. This framework 15 | will automatically download the libPhoneNumber-iOS cocoapod if not already included in the project podfile. 16 | 17 | #### Using [CocoaPods](http://cocoapods.org/?q=libPhoneNumber-iOS) 18 | ``` 19 | pod 'libPhoneNumberShortNumber', :git => 'https://github.com/iziz/libPhoneNumber-iOS' 20 | ``` 21 | 22 | And set the **Embedded Content Contains Swift** to "Yes" in your build settings. 23 | 24 | See sample test code from 25 | > [libPhoneNumber-iOS/libPhoneNumberShortNumberTests/ ... Test.m] (https://github.com/iziz/libPhoneNumber-iOS/tree/master/libPhoneNumberShortNumberTests) 26 | 27 | 28 | #### with Swift 29 | ##### Case (1) with Framework 30 | ``` 31 | import libPhoneNumberShortNumber 32 | ``` 33 | 34 | ##### Case (2) with Bridging-Header 35 | ```obj-c 36 | // Manually added 37 | #import "NBShortNumberUtil.h" 38 | #import "NBPhoneNumber.h" 39 | 40 | // CocoaPods (check your library path) 41 | #import "libPhoneNumberShortNumber/NBShortNumberUtil.h" 42 | #import "libPhoneNumberShortNumber/NBPhoneNumber.h" 43 | 44 | // add more if you want... 45 | ``` 46 | 47 | ## Usage - **NBShortNumberUtil** 48 | ```obj-c 49 | NBShortNumberUtil *shortNumberUtil = [NBShortNumberUtil sharedInstance]; 50 | 51 | // possibleNumber : +33123456 52 | NSLog(@"Is possible short number: %d", 53 | [shortNumberUtil isPossibleShortNumber:possibleNumber]); 54 | // validNumber : +331010 55 | NSLog(@"Is valid short number: %d", [shortNumberUtil isValidShortNumber:validNumber]); 56 | 57 | // carrierSpecificNumber : +133669 58 | NSLog(@"Is carrier specific number: %d", 59 | [shortNumberUtil isPhoneNumberCarrierSpecific:carrierSpecificNumber]); 60 | 61 | // notCarrierSpecific : +1911 62 | NSLog(@"Is carrier specific number: %d", 63 | [shortNumberUtil isPhoneNumberCarrierSpecific:notCarrierSpecific]); 64 | 65 | // premiumRateNumber : +3336665 66 | NSLog(@"Premium Rate Phone Number: %lu", 67 | [shortNumberUtil expectedCostOfPhoneNumber:premiumRateNumber forRegion:@"FR"]); 68 | 69 | // emergencyNumber 70 | NSLog(@"Connects to Emergency Number From String: %d", 71 | [shortNumberUtil connectsToEmergencyNumberFromString:@"911" forRegion:@"US"]); 72 | 73 | ``` 74 | ##### Output 75 | ``` 76 | Is possible short number: 1 77 | Is valid short number: 1 78 | Is carrier specific number: 1 79 | Is carrier specific number: 0 80 | Premium Rate Phone Number: 3 81 | Connects to Emergency Number From String: 1 82 | ``` 83 | 84 | ##### Visit [libphonenumber](https://github.com/google/libphonenumber) for more information 85 | -------------------------------------------------------------------------------- /libPhoneNumber/NBRegExMatcher.m: -------------------------------------------------------------------------------- 1 | // 2 | // NBRegExMatcher.m 3 | // libPhoneNumber 4 | // 5 | // Created by Paween Itthipalkul on 11/29/17. 6 | // Copyright © 2017 Google LLC. All rights reserved. 7 | // 8 | 9 | #import "NBRegExMatcher.h" 10 | #import "NBPhoneNumberDesc.h" 11 | #import "NBRegularExpressionCache.h" 12 | #import "NBPhoneNumberUtil.h" 13 | 14 | // Expose this method to get a modified RegEx to cover the entire RegEx. 15 | // Though all RegEx methods and functionalities should be moved to either this class, or a separate 16 | // class rather than in NBPhoneNumberUtil. 17 | @interface NBPhoneNumberUtil() 18 | - (NSRegularExpression *)entireRegularExpressionWithPattern:(NSString *)regexPattern 19 | options:(NSRegularExpressionOptions)options 20 | error:(NSError **)error; 21 | 22 | @end 23 | 24 | @implementation NBRegExMatcher 25 | 26 | - (BOOL)matchNationalNumber:(NSString *)string 27 | phoneNumberDesc:(NBPhoneNumberDesc *)numberDesc 28 | allowsPrefixMatch:(BOOL)allowsPrefixMatch { 29 | NSString *nationalNumberPattern = numberDesc.nationalNumberPattern; 30 | 31 | // We don't want to consider it a prefix match when matching non-empty input against an empty 32 | // pattern. 33 | if (nationalNumberPattern.length == 0) { 34 | return NO; 35 | } 36 | 37 | NSRegularExpression *regEx = 38 | [[NBPhoneNumberUtil sharedInstance] entireRegularExpressionWithPattern:nationalNumberPattern 39 | options:kNilOptions 40 | error:nil]; 41 | 42 | if (regEx == nil) { 43 | NSAssert(true, @"Regular expression shouldn't be nil"); 44 | return NO; 45 | } 46 | 47 | NSRange wholeStringRange = NSMakeRange(0, string.length); 48 | 49 | // Prefix match (lookingAt()) search 50 | NSRegularExpression *prefixRegEx = 51 | [[NBRegularExpressionCache sharedInstance] regularExpressionForPattern:nationalNumberPattern 52 | error:NULL]; 53 | if (prefixRegEx == nil) { 54 | NSAssert(true, @"Regular expression shouldn't be nil"); 55 | return NO; 56 | } 57 | 58 | NSTextCheckingResult *prefixResult = [prefixRegEx firstMatchInString:string 59 | options:NSMatchingAnchored 60 | range:wholeStringRange]; 61 | if (prefixResult.numberOfRanges <= 0) { 62 | // No prefix match found. 63 | return NO; 64 | } else { 65 | // Found prefix match, but need to see if exact match works as well. 66 | // Exact match (matches()) search. 67 | NSTextCheckingResult *exactResult = [regEx firstMatchInString:string 68 | options:NSMatchingAnchored 69 | range:wholeStringRange]; 70 | 71 | return (allowsPrefixMatch || exactResult.numberOfRanges > 0); 72 | } 73 | } 74 | 75 | @end 76 | -------------------------------------------------------------------------------- /libPhoneNumber.xcodeproj/xcshareddata/xcschemes/libPhoneNumber.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 34 | 40 | 41 | 42 | 43 | 44 | 54 | 55 | 61 | 62 | 68 | 69 | 70 | 71 | 73 | 74 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /libPhoneNumber.xcodeproj/xcshareddata/xcschemes/libPhoneNumberGeocoding.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 53 | 54 | 60 | 61 | 67 | 68 | 69 | 70 | 72 | 73 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /libPhoneNumber.xcodeproj/xcshareddata/xcschemes/libPhoneNumberShortNumber.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 53 | 54 | 60 | 61 | 67 | 68 | 69 | 70 | 72 | 73 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /libPhoneNumber/NBPhoneNumberDefines.h: -------------------------------------------------------------------------------- 1 | // 2 | // NBPhoneNumberDefines.h 3 | // libPhoneNumber 4 | // 5 | // 6 | 7 | #import 8 | 9 | #ifndef libPhoneNumber_NBPhoneNumberDefines_h 10 | #define libPhoneNumber_NBPhoneNumberDefines_h 11 | 12 | #define NB_YES [NSNumber numberWithBool:YES] 13 | #define NB_NO [NSNumber numberWithBool:NO] 14 | 15 | #pragma mark - Enum - 16 | 17 | typedef NS_ENUM(NSInteger, NBEPhoneNumberFormat) { 18 | NBEPhoneNumberFormatE164 = 0, 19 | NBEPhoneNumberFormatINTERNATIONAL = 1, 20 | NBEPhoneNumberFormatNATIONAL = 2, 21 | NBEPhoneNumberFormatRFC3966 = 3 22 | }; 23 | 24 | typedef NS_ENUM(NSInteger, NBEPhoneNumberType) { 25 | NBEPhoneNumberTypeFIXED_LINE = 0, 26 | NBEPhoneNumberTypeMOBILE = 1, 27 | // In some regions (e.g. the USA), it is impossible to distinguish between 28 | // fixed-line and mobile numbers by looking at the phone number itself. 29 | NBEPhoneNumberTypeFIXED_LINE_OR_MOBILE = 2, 30 | // Freephone lines 31 | NBEPhoneNumberTypeTOLL_FREE = 3, 32 | NBEPhoneNumberTypePREMIUM_RATE = 4, 33 | // The cost of this call is shared between the caller and the recipient, and 34 | // is hence typically less than PREMIUM_RATE calls. See 35 | // http://en.wikipedia.org/wiki/Shared_Cost_Service for more information. 36 | NBEPhoneNumberTypeSHARED_COST = 5, 37 | // Voice over IP numbers. This includes TSoIP (Telephony Service over IP). 38 | NBEPhoneNumberTypeVOIP = 6, 39 | // A personal number is associated with a particular person, and may be routed 40 | // to either a MOBILE or FIXED_LINE number. Some more information can be found 41 | // here = http://en.wikipedia.org/wiki/Personal_Numbers 42 | NBEPhoneNumberTypePERSONAL_NUMBER = 7, 43 | NBEPhoneNumberTypePAGER = 8, 44 | // Used for 'Universal Access Numbers' or 'Company Numbers'. They may be 45 | // further routed to specific offices, but allow one number to be used for a 46 | // company. 47 | NBEPhoneNumberTypeUAN = 9, 48 | // Used for 'Voice Mail Access Numbers'. 49 | NBEPhoneNumberTypeVOICEMAIL = 10, 50 | // A phone number is of type UNKNOWN when it does not fit any of the known 51 | // patterns for a specific region. 52 | NBEPhoneNumberTypeUNKNOWN = -1 53 | }; 54 | 55 | typedef NS_ENUM(NSInteger, NBEMatchType) { 56 | NBEMatchTypeNOT_A_NUMBER = 0, 57 | NBEMatchTypeNO_MATCH = 1, 58 | NBEMatchTypeSHORT_NSN_MATCH = 2, 59 | NBEMatchTypeNSN_MATCH = 3, 60 | NBEMatchTypeEXACT_MATCH = 4 61 | }; 62 | 63 | typedef NS_ENUM(NSInteger, NBEValidationResult) { 64 | NBEValidationResultINVALID_LENGTH = -1, 65 | NBEValidationResultUNKNOWN = 0, 66 | NBEValidationResultIS_POSSIBLE = 1, 67 | NBEValidationResultINVALID_COUNTRY_CODE = 2, 68 | NBEValidationResultTOO_SHORT = 3, 69 | NBEValidationResultTOO_LONG = 4, 70 | NBEValidationResultIS_POSSIBLE_LOCAL_ONLY = 5 71 | }; 72 | 73 | typedef NS_ENUM(NSInteger, NBECountryCodeSource) { 74 | NBECountryCodeSourceFROM_NUMBER_WITH_PLUS_SIGN = 1, 75 | NBECountryCodeSourceFROM_NUMBER_WITH_IDD = 5, 76 | NBECountryCodeSourceFROM_NUMBER_WITHOUT_PLUS_SIGN = 10, 77 | NBECountryCodeSourceFROM_DEFAULT_COUNTRY = 20 78 | }; 79 | 80 | extern NSString* const NB_UNKNOWN_REGION; 81 | extern NSString* const NB_NON_BREAKING_SPACE; 82 | extern NSString* const NB_PLUS_CHARS; 83 | extern NSString* const NB_VALID_DIGITS_STRING; 84 | extern NSString* const NB_REGION_CODE_FOR_NON_GEO_ENTITY; 85 | 86 | #endif 87 | -------------------------------------------------------------------------------- /libPhoneNumberGeocoding/README.md: -------------------------------------------------------------------------------- 1 | # **libPhoneNumberGeocoding for iOS** 2 | 3 | - NBPhoneNumberOfflineGeocoder 4 | 5 | > ARC only 6 | 7 | ## Update Log 8 | [https://github.com/iziz/libPhoneNumber-iOS/wiki/Update-Log](https://github.com/iziz/libPhoneNumber-iOS/wiki/Update-Log) 9 | 10 | 11 | ## Install 12 | 13 | The libPhoneNumberGeocoding pod requires the libPhoneNumber-iOS framework in order to build. This framework 14 | will automatically download the libPhoneNumber-iOS cocoapod if not already included in the project podfile. 15 | 16 | #### Using [CocoaPods](http://cocoapods.org/?q=libPhoneNumber-iOS) 17 | ``` 18 | pod 'libPhoneNumberGeocoding', :git => 'https://github.com/iziz/libPhoneNumber-iOS' 19 | ``` 20 | 21 | And set the **Embedded Content Contains Swift** to "Yes" in your build settings. 22 | 23 | See sample test code from 24 | > [libPhoneNumber-iOS/libPhoneNumberGeocodingTests/ ... Test.m] (https://github.com/iziz/libPhoneNumber-iOS/tree/master/libPhoneNumberGeocodingTests) 25 | 26 | 27 | #### with Swift 28 | ##### Case (1) with Framework 29 | ``` 30 | import libPhoneNumberGeocoding 31 | ``` 32 | 33 | ##### Case (2) with Bridging-Header 34 | ```obj-c 35 | // Manually added 36 | #import "NBPhoneNumberOfflineGeocoder.h" 37 | #import "NBPhoneNumber.h" 38 | 39 | // CocoaPods (check your library path) 40 | #import "libPhoneNumberGeocoding/NBPhoneNumberOfflineGeocoder.h" 41 | #import "libPhoneNumberGeocoding/NBPhoneNumber.h" 42 | 43 | // add more if you want... 44 | ``` 45 | 46 | ## Usage - **NBPhoneNumberOfflineGeocoder** 47 | ```obj-c 48 | NBPhoneNumberOfflineGeocoder *geocoder = [[NBPhoneNumberOfflineGeocoder alloc] init]; 49 | 50 | // unitedStatesPhoneNumber : +16509601234 51 | NSLog(@Valid US Number: "%@", [geocoder descriptionForNumber: unitedStatesPhoneNumber 52 | withLanguageCode: @"en"]); 53 | // Convenience Method 54 | NSLog(@"Convenience Method: %@", [geocoder descriptionForNumber: unitedStatesPhoneNumber]); 55 | 56 | // United States Phone Number, with user located in Italy and device language set to Spanish 57 | NSLog(@"Method using Spanish Language: %@", [geocoder descriptionForNumber: unitedStatesPhoneNumber 58 | withLanguageCode: @"es" 59 | withUserRegion: @"IT"]); 60 | 61 | // southKoreaPhoneNumber : +8222123456 62 | NSLog(@"South Korea Phone Number: %@", [geocoder descriptionForNumber: southKoreaPhoneNumber 63 | withLanguageCode: @"en"]); 64 | NSLog(@"South Korea Phone Number: %@", [geocoder descriptionForNumber: southKoreanPhoneNumber 65 | withLanguageCode: @"ko"]); 66 | 67 | // invalidUSPhoneNumber : +1123456789 68 | NSLog(@"%@", [geocoder descriptionForNumber: invalidUSPhoneNumber 69 | withLanguageCode: @"en"]); 70 | 71 | // Non-geographical South Korea Phone Number: +82101234567 72 | NSLog(@"Non-geographical number: %@", [geocoder descriptionForNumber: southKoreaMobilePhone]); 73 | ``` 74 | ##### Output 75 | ``` 76 | Valid US Number: Mountain View, CA 77 | Convenience Method: Mountain View, CA 78 | Method using Spanish Language: Estados Unidos 79 | South Korea Phone Number: Seoul 80 | South Korea Phone Number: 서울 81 | (null) 82 | Non-geographical number: South Korea 83 | ``` 84 | 85 | ##### Visit [libphonenumber](https://github.com/google/libphonenumber) for more information 86 | -------------------------------------------------------------------------------- /libPhoneNumber/NBPhoneNumberDesc.m: -------------------------------------------------------------------------------- 1 | // 2 | // NBPhoneNumberDesc.m 3 | // libPhoneNumber 4 | // 5 | // 6 | 7 | #import "NBPhoneNumberDesc.h" 8 | #import "NSArray+NBAdditions.h" 9 | 10 | @implementation NBPhoneNumberDesc 11 | 12 | - (instancetype)initWithEntry:(NSArray *)entry { 13 | self = [super init]; 14 | if (self && entry != nil) { 15 | _nationalNumberPattern = [entry nb_safeStringAtIndex:2]; 16 | _possibleNumberPattern = [entry nb_safeStringAtIndex:3]; 17 | _possibleLength = [entry nb_safeArrayAtIndex:9]; 18 | _possibleLengthLocalOnly = [entry nb_safeArrayAtIndex:10]; 19 | _exampleNumber = [entry nb_safeStringAtIndex:6]; 20 | _nationalNumberMatcherData = [entry nb_safeDataAtIndex:7]; 21 | _possibleNumberMatcherData = [entry nb_safeDataAtIndex:8]; 22 | } 23 | return self; 24 | } 25 | 26 | - (NSString *)description { 27 | return [NSString 28 | stringWithFormat:@"nationalNumberPattern[%@] possibleNumberPattern[%@] possibleLength[%@] " 29 | @"possibleLengthLocalOnly[%@] exampleNumber[%@]", 30 | self.nationalNumberPattern, self.possibleNumberPattern, self.possibleLength, 31 | self.possibleLengthLocalOnly, self.exampleNumber]; 32 | } 33 | 34 | #ifdef NB_USE_EXTENSIONS 35 | // We believe these methods are unused. 36 | // If you would like them back (not behind a flag) please file a bug with a reason for needing 37 | // them. 38 | 39 | - (instancetype)initWithCoder:(NSCoder *)coder { 40 | if (self = [super init]) { 41 | _nationalNumberPattern = [coder decodeObjectForKey:@"nationalNumberPattern"]; 42 | _possibleNumberPattern = [coder decodeObjectForKey:@"possibleNumberPattern"]; 43 | _possibleLength = [coder decodeObjectForKey:@"possibleLength"]; 44 | _possibleLengthLocalOnly = [coder decodeObjectForKey:@"possibleLengthLocalOnly"]; 45 | _exampleNumber = [coder decodeObjectForKey:@"exampleNumber"]; 46 | _nationalNumberMatcherData = [coder decodeObjectForKey:@"nationalNumberMatcherData"]; 47 | _possibleNumberMatcherData = [coder decodeObjectForKey:@"possibleNumberMatcherData"]; 48 | } 49 | return self; 50 | } 51 | 52 | - (void)encodeWithCoder:(NSCoder *)coder { 53 | [coder encodeObject:self.nationalNumberPattern forKey:@"nationalNumberPattern"]; 54 | [coder encodeObject:self.possibleNumberPattern forKey:@"possibleNumberPattern"]; 55 | [coder encodeObject:self.possibleLength forKey:@"possibleLength"]; 56 | [coder encodeObject:self.possibleLengthLocalOnly forKey:@"possibleLengthLocalOnly"]; 57 | [coder encodeObject:self.exampleNumber forKey:@"exampleNumber"]; 58 | [coder encodeObject:self.nationalNumberMatcherData forKey:@"nationalNumberMatcherData"]; 59 | [coder encodeObject:self.possibleNumberMatcherData forKey:@"possibleNumberMatcherData"]; 60 | } 61 | 62 | - (id)copyWithZone:(NSZone *)zone { 63 | return self; 64 | } 65 | 66 | - (BOOL)isEqual:(id)object { 67 | if ([object isKindOfClass:[NBPhoneNumberDesc class]] == NO) { 68 | return NO; 69 | } 70 | 71 | NBPhoneNumberDesc *other = object; 72 | return [self.nationalNumberPattern isEqual:other.nationalNumberPattern] && 73 | [self.possibleNumberPattern isEqual:other.possibleNumberPattern] && 74 | [self.possibleLength isEqual:other.possibleLength] && 75 | [self.possibleLengthLocalOnly isEqual:other.possibleLengthLocalOnly] && 76 | [self.exampleNumber isEqual:other.exampleNumber] && 77 | [self.nationalNumberMatcherData isEqualToData:other.nationalNumberMatcherData] && 78 | [self.possibleNumberMatcherData isEqualToData:other.possibleNumberMatcherData]; 79 | } 80 | 81 | #endif // NB_USE_EXTENSIONS 82 | 83 | @end 84 | -------------------------------------------------------------------------------- /libPhoneNumber-GeocodingParser/libPhoneNumber-GeocodingParser.xcodeproj/xcshareddata/xcschemes/libPhoneNumber-GeocodingParser.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 45 | 51 | 52 | 53 | 54 | 57 | 58 | 59 | 60 | 66 | 68 | 74 | 75 | 76 | 77 | 79 | 80 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /libPhoneNumber-Demo/libPhoneNumber-Demo/ShortNumberUtilView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ShortNumberUtilView.swift 3 | // libPhoneNumber-Demo 4 | // 5 | // Created by Rastaar Haghi on 7/20/20. 6 | // Copyright © 2020 Google LLC. All rights reserved. 7 | // 8 | 9 | import SwiftUI 10 | import libPhoneNumberShortNumber 11 | 12 | #if canImport(libPhoneNumber) 13 | import libPhoneNumber 14 | #elseif canImport(libPhoneNumber_iOS) 15 | import libPhoneNumber_iOS 16 | #endif 17 | 18 | struct ShortNumberUtilView: View { 19 | @State private var phoneNumber: String = "" 20 | @State private var isValidShortNumber: Bool = false 21 | @State private var isEmergencyNumber: Bool = false 22 | @State private var estimatedCostOfCall: NBEShortNumberCost? 23 | @State private var searchMade: Bool = false 24 | 25 | var body: some View { 26 | VStack { 27 | Form { 28 | Section(header: Text("Phone Number")) { 29 | TextField("Ex: 19098611234", text: $phoneNumber) 30 | .textFieldStyle(RoundedBorderTextFieldStyle()) 31 | .padding() 32 | } 33 | Button( 34 | action: { 35 | self.parsePhoneNumber() 36 | }, 37 | label: { 38 | Text("Parse Short Number") 39 | }) 40 | } 41 | if searchMade { 42 | if isValidShortNumber { 43 | SuccessResultView 44 | } else { 45 | FailedResultView 46 | } 47 | } 48 | } 49 | .navigationBarTitle(Text("Short Number Util Parser")) 50 | } 51 | 52 | var SuccessResultView: some View { 53 | Form { 54 | Section(header: Text("Phone Number Validation")) { 55 | Text("Valid Short Number ✅") 56 | } 57 | Section(header: Text("Emergency Number")) { 58 | if isEmergencyNumber { 59 | Text(phoneNumber) + Text(" is an emergency number 🚨") 60 | } else { 61 | Text(phoneNumber) + Text(" is not an emergency number") 62 | } 63 | } 64 | 65 | Section(header: Text("Expected Cost of Short Number")) { 66 | if estimatedCostOfCall == NBEShortNumberCost(rawValue: 1) { 67 | Text("Toll Free Number") 68 | } else if estimatedCostOfCall == NBEShortNumberCost(rawValue: 2) { 69 | Text("Standard Rate Number") 70 | } else if estimatedCostOfCall == NBEShortNumberCost(rawValue: 3) { 71 | Text("Premium Rate Number") 72 | } else { 73 | Text("Unknown Number Cost") 74 | } 75 | } 76 | } 77 | } 78 | 79 | var FailedResultView: some View { 80 | Form { 81 | Section(header: Text("Phone Number Validation")) { 82 | Text("Invalid Short Number ❌") 83 | } 84 | } 85 | } 86 | } 87 | 88 | extension ShortNumberUtilView { 89 | func parsePhoneNumber() { 90 | do { 91 | self.searchMade = true 92 | let parsedPhoneNumber: NBPhoneNumber = 93 | try NBPhoneNumberUtil.sharedInstance().parse(self.phoneNumber, defaultRegion: Locale.current.regionCode!) 94 | self.isValidShortNumber = NBShortNumberUtil.sharedInstance().isValidShortNumber(parsedPhoneNumber) 95 | self.isEmergencyNumber = 96 | NBShortNumberUtil.sharedInstance().isEmergencyNumber( 97 | self.phoneNumber, 98 | forRegion: Locale.current.regionCode!) 99 | self.estimatedCostOfCall = NBShortNumberUtil.sharedInstance().expectedCost(of: parsedPhoneNumber) 100 | } catch { 101 | print(error) 102 | } 103 | } 104 | } 105 | 106 | struct ShortNumberUtilView_Previews: PreviewProvider { 107 | static var previews: some View { 108 | ShortNumberUtilView() 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /libPhoneNumber-Demo/libPhoneNumber-Demo/PhoneUtilView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PhoneUtilView.swift 3 | // libPhoneNumber-Demo 4 | // 5 | // Created by Rastaar Haghi on 7/17/20. 6 | // Copyright © 2020 Google LLC. All rights reserved. 7 | // 8 | 9 | import SwiftUI 10 | import libPhoneNumberGeocoding 11 | 12 | #if canImport(libPhoneNumber) 13 | import libPhoneNumber 14 | #elseif canImport(libPhoneNumber_iOS) 15 | import libPhoneNumber_iOS 16 | #endif 17 | 18 | struct PhoneUtilView: View { 19 | @State private var countryCode: String = "" 20 | @State private var nationalNumber: String = "" 21 | @State private var phoneNumber: String = "" 22 | @State private var isValidNumber: Bool = false 23 | @State private var searchMade: Bool = false 24 | @State private var formatSelection = 0 25 | @State private var formattedPhoneNumber: String = "" 26 | 27 | var body: some View { 28 | VStack { 29 | Form { 30 | Section(header: Text("Phone Number")) { 31 | TextField("Ex: 19098611234", text: $phoneNumber) 32 | .textFieldStyle(RoundedBorderTextFieldStyle()) 33 | .padding() 34 | } 35 | 36 | Section(header: Text("(Optional) Format Phone Number")) { 37 | Picker("Locale Options", selection: $formatSelection) { 38 | ForEach(formatOptions.indices, id: \.self) { index in 39 | Text(formatOptions[index]) 40 | .tag(index) 41 | } 42 | } 43 | } 44 | 45 | Button( 46 | action: { 47 | self.parsePhoneNumber() 48 | }, 49 | label: { 50 | Text("Parse Phone Number") 51 | }) 52 | } 53 | if searchMade { 54 | if isValidNumber { 55 | SuccessResultView 56 | } else { 57 | FailedResultView 58 | } 59 | } 60 | } 61 | .navigationBarTitle(Text("PhoneUtil Parser")) 62 | } 63 | 64 | var SuccessResultView: some View { 65 | Form { 66 | Section(header: Text("Phone Number Validation")) { 67 | Text("Valid Number ✅") 68 | } 69 | if formattedPhoneNumber != "" { 70 | Section(header: Text("Formatted Phone Number")) { 71 | Text(self.formattedPhoneNumber) 72 | } 73 | } 74 | Section(header: Text("Country Code")) { 75 | Text(self.countryCode) 76 | } 77 | Section(header: Text("National Number")) { 78 | Text(self.nationalNumber) 79 | } 80 | } 81 | } 82 | 83 | var FailedResultView: some View { 84 | Form { 85 | Section(header: Text("Phone Number Validation")) { 86 | Text("Invalid Phone Number ❌") 87 | } 88 | } 89 | } 90 | } 91 | 92 | extension PhoneUtilView { 93 | func parsePhoneNumber() { 94 | do { 95 | self.searchMade = true 96 | let parsedPhoneNumber: NBPhoneNumber = 97 | try NBPhoneNumberUtil.sharedInstance().parse(self.phoneNumber, defaultRegion: Locale.current.regionCode!) 98 | self.isValidNumber = NBPhoneNumberUtil.sharedInstance().isValidNumber(parsedPhoneNumber) 99 | self.countryCode = parsedPhoneNumber.countryCode.stringValue 100 | self.nationalNumber = parsedPhoneNumber.nationalNumber.stringValue 101 | if self.formatSelection != 0 { 102 | self.formattedPhoneNumber = try 103 | NBPhoneNumberUtil.sharedInstance().format( 104 | parsedPhoneNumber, 105 | numberFormat: 106 | NBEPhoneNumberFormat(rawValue: self.formatSelection - 1)!) 107 | } else { 108 | self.formattedPhoneNumber = "" 109 | } 110 | 111 | } catch { 112 | print(error) 113 | } 114 | } 115 | } 116 | 117 | struct PhoneUtilView_Previews: PreviewProvider { 118 | static var previews: some View { 119 | PhoneUtilView() 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /libPhoneNumberTests/NBPhoneNumberParsingPerfTest.m: -------------------------------------------------------------------------------- 1 | // 2 | // NBPhoneNumberParsingPerfTest.m 3 | // libPhoneNumberiOSTests 4 | // 5 | // Created by Paween Itthipalkul on 2/1/18. 6 | // Copyright © 2018 Google LLC. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import "NBMetadataHelper.h" 12 | #import "NBPhoneMetaData.h" 13 | 14 | #import "NBNumberFormat.h" 15 | #import "NBPhoneNumber.h" 16 | #import "NBPhoneNumberDesc.h" 17 | #import "NBPhoneNumberUtil.h" 18 | 19 | @interface NBExampleNumber : NSObject 20 | 21 | @property(nonatomic, strong) NSString *phoneNumber; 22 | @property(nonatomic, strong) NSString *baseRegionCode; 23 | 24 | - (instancetype)initWithPhoneNumber:(NSString *)phoneNumber 25 | baseRegionCode:(NSString *)baseRegionCode; 26 | 27 | @end 28 | 29 | @implementation NBExampleNumber 30 | - (instancetype)initWithPhoneNumber:(NSString *)phoneNumber 31 | baseRegionCode:(NSString *)baseRegionCode { 32 | self = [super init]; 33 | if (self != nil) { 34 | _phoneNumber = phoneNumber; 35 | _baseRegionCode = baseRegionCode; 36 | } 37 | 38 | return self; 39 | } 40 | @end 41 | 42 | @interface NBPhoneNumberParsingPerfTest : XCTestCase 43 | @end 44 | 45 | @implementation NBPhoneNumberParsingPerfTest 46 | 47 | #if PERF_TEST 48 | 49 | - (void)testParsing { 50 | NSArray *regionCodes = 51 | [[[[NBMetadataHelper alloc] init] countryCodeToCountryNumberDictionary] allKeys]; 52 | 53 | NSMutableArray *exampleNumbers = [[NSMutableArray alloc] init]; 54 | 55 | NBPhoneNumberUtil *util = [NBPhoneNumberUtil sharedInstance]; 56 | 57 | for (NSString *regionCode in regionCodes) { 58 | NBPhoneNumber *phoneNumber = [util getExampleNumber:regionCode error:nil]; 59 | if (phoneNumber != nil) { 60 | NSString *e164 = [util format:phoneNumber numberFormat:NBEPhoneNumberFormatE164 error:nil]; 61 | NBExampleNumber *e164Sample = [[NBExampleNumber alloc] initWithPhoneNumber:e164 62 | baseRegionCode:regionCode]; 63 | [exampleNumbers addObject:e164Sample]; 64 | 65 | NSString *national = [util format:phoneNumber 66 | numberFormat:NBEPhoneNumberFormatNATIONAL 67 | error:nil]; 68 | NBExampleNumber *nationalSample = [[NBExampleNumber alloc] initWithPhoneNumber:national 69 | baseRegionCode:regionCode]; 70 | [exampleNumbers addObject:nationalSample]; 71 | 72 | // intl format sample. 73 | NSString *intl = [util format:phoneNumber 74 | numberFormat:NBEPhoneNumberFormatINTERNATIONAL 75 | error:nil]; 76 | NBExampleNumber *intlSample = [[NBExampleNumber alloc] initWithPhoneNumber:intl 77 | baseRegionCode:regionCode]; 78 | [exampleNumbers addObject:intlSample]; 79 | } 80 | } 81 | 82 | for (int i = 0; i < 5; i++) { 83 | [exampleNumbers addObjectsFromArray:exampleNumbers]; 84 | } 85 | 86 | [self measureBlock:^{ 87 | for (NBExampleNumber *example in exampleNumbers) { 88 | [util parseAndKeepRawInput:example.phoneNumber 89 | defaultRegion:example.baseRegionCode 90 | error:nil]; 91 | } 92 | }]; 93 | } 94 | 95 | - (void)testParsingWithDefaultRegion { 96 | NBPhoneNumberUtil *util = [NBPhoneNumberUtil sharedInstance]; 97 | 98 | NSString *exampleNumber = @"+5491187654321"; 99 | [self measureBlock:^{ 100 | for (int i = 0; i < 10000; i++) { 101 | [util parseWithPhoneCarrierRegion:exampleNumber error: nil]; 102 | } 103 | }]; 104 | } 105 | 106 | #endif // PERF_TEST 107 | 108 | @end 109 | -------------------------------------------------------------------------------- /libPhoneNumber/NBNumberFormat.m: -------------------------------------------------------------------------------- 1 | // 2 | // NBPhoneNumberFormat.m 3 | // libPhoneNumber 4 | // 5 | // 6 | 7 | #import "NBNumberFormat.h" 8 | #import "NSArray+NBAdditions.h" 9 | 10 | @implementation NBNumberFormat 11 | 12 | - (instancetype)initWithPattern:(NSString *)pattern 13 | withFormat:(NSString *)format 14 | withLeadingDigitsPatterns:(NSArray *)leadingDigitsPatterns 15 | withNationalPrefixFormattingRule:(NSString *)nationalPrefixFormattingRule 16 | whenFormatting:(BOOL)nationalPrefixOptionalWhenFormatting 17 | withDomesticCarrierCodeFormattingRule:(NSString *)domesticCarrierCodeFormattingRule { 18 | self = [super init]; 19 | if (self) { 20 | _pattern = pattern; 21 | _format = format; 22 | _leadingDigitsPatterns = leadingDigitsPatterns; 23 | _nationalPrefixFormattingRule = nationalPrefixFormattingRule; 24 | _nationalPrefixOptionalWhenFormatting = nationalPrefixOptionalWhenFormatting; 25 | _domesticCarrierCodeFormattingRule = domesticCarrierCodeFormattingRule; 26 | } 27 | return self; 28 | } 29 | 30 | - (instancetype)initWithEntry:(NSArray *)entry { 31 | self = [super init]; 32 | if (self && entry != nil) { 33 | _pattern = [entry nb_safeStringAtIndex:1]; 34 | _format = [entry nb_safeStringAtIndex:2]; 35 | _leadingDigitsPatterns = [entry nb_safeArrayAtIndex:3]; 36 | _nationalPrefixFormattingRule = [entry nb_safeStringAtIndex:4]; 37 | _nationalPrefixOptionalWhenFormatting = [[entry nb_safeNumberAtIndex:6] boolValue]; 38 | _domesticCarrierCodeFormattingRule = [entry nb_safeStringAtIndex:5]; 39 | } 40 | return self; 41 | } 42 | 43 | - (NSString *)description { 44 | return [NSString 45 | stringWithFormat: 46 | @"[pattern:%@, format:%@, leadingDigitsPattern:%@, nationalPrefixFormattingRule:%@, " 47 | @"nationalPrefixOptionalWhenFormatting:%@, domesticCarrierCodeFormattingRule:%@]", 48 | self.pattern, self.format, self.leadingDigitsPatterns, self.nationalPrefixFormattingRule, 49 | self.nationalPrefixOptionalWhenFormatting ? @"Y" : @"N", 50 | self.domesticCarrierCodeFormattingRule]; 51 | } 52 | 53 | - (id)copyWithZone:(NSZone *)zone { 54 | return [[NBNumberFormat alloc] initWithPattern:self.pattern 55 | withFormat:self.format 56 | withLeadingDigitsPatterns:self.leadingDigitsPatterns 57 | withNationalPrefixFormattingRule:self.nationalPrefixFormattingRule 58 | whenFormatting:self.nationalPrefixOptionalWhenFormatting 59 | withDomesticCarrierCodeFormattingRule:self.domesticCarrierCodeFormattingRule]; 60 | } 61 | 62 | #ifdef NB_USE_EXTENSIONS 63 | // We believe these methods are unused. 64 | // If you would like them back (not behind a flag) please file a bug with a reason for needing 65 | // them. 66 | 67 | - (instancetype)initWithCoder:(NSCoder *)coder { 68 | if (self = [super init]) { 69 | _pattern = [coder decodeObjectForKey:@"pattern"]; 70 | _format = [coder decodeObjectForKey:@"format"]; 71 | _leadingDigitsPatterns = [coder decodeObjectForKey:@"leadingDigitsPatterns"]; 72 | _nationalPrefixFormattingRule = [coder decodeObjectForKey:@"nationalPrefixFormattingRule"]; 73 | _nationalPrefixOptionalWhenFormatting = 74 | [[coder decodeObjectForKey:@"nationalPrefixOptionalWhenFormatting"] boolValue]; 75 | _domesticCarrierCodeFormattingRule = 76 | [coder decodeObjectForKey:@"domesticCarrierCodeFormattingRule"]; 77 | } 78 | return self; 79 | } 80 | 81 | - (void)encodeWithCoder:(NSCoder *)coder { 82 | [coder encodeObject:self.pattern forKey:@"pattern"]; 83 | [coder encodeObject:self.format forKey:@"format"]; 84 | [coder encodeObject:self.leadingDigitsPatterns forKey:@"leadingDigitsPatterns"]; 85 | [coder encodeObject:self.nationalPrefixFormattingRule forKey:@"nationalPrefixFormattingRule"]; 86 | [coder encodeObject:[NSNumber numberWithBool:self.nationalPrefixOptionalWhenFormatting] 87 | forKey:@"nationalPrefixOptionalWhenFormatting"]; 88 | [coder encodeObject:self.domesticCarrierCodeFormattingRule 89 | forKey:@"domesticCarrierCodeFormattingRule"]; 90 | } 91 | 92 | #endif // NB_USE_EXTENSIONS 93 | 94 | @end 95 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.5 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | import PackageDescription 4 | 5 | let package = Package( 6 | name: "libPhoneNumber", 7 | platforms: [ 8 | .macOS(.v10_13), 9 | .macCatalyst(.v13), 10 | .iOS(.v12), 11 | .tvOS(.v12), 12 | .watchOS(.v4) 13 | ], 14 | products: [ 15 | .library( 16 | name: "libPhoneNumber", 17 | targets: ["libPhoneNumber"] 18 | ), 19 | .library( 20 | name: "libPhoneNumberGeocoding", 21 | targets: ["libPhoneNumberGeocoding"] 22 | ), 23 | .library( 24 | name: "libPhoneNumberShortNumber", 25 | targets: ["libPhoneNumberShortNumber"] 26 | ) 27 | ], 28 | targets: [ 29 | .target( 30 | name: "libPhoneNumberTestsCommon", 31 | path: "libPhoneNumberTestsCommon", 32 | resources: [ 33 | .copy("libPhoneNumberMetaDataForTesting.zip") 34 | ], 35 | publicHeadersPath: "." 36 | ), 37 | .target( 38 | name: "libPhoneNumberInternal", 39 | path: "libPhoneNumberInternal", 40 | publicHeadersPath: "." 41 | ), 42 | .target( 43 | name: "libPhoneNumber", 44 | dependencies: ["libPhoneNumberInternal"], 45 | path: "libPhoneNumber", 46 | exclude: ["Info.plist"], 47 | resources: [ 48 | .process("PrivacyInfo.xcprivacy") 49 | ], 50 | publicHeadersPath: ".", 51 | cSettings: [ 52 | .headerSearchPath("Internal") 53 | ], 54 | linkerSettings: [ 55 | .linkedFramework("Contacts", .when(platforms: [.iOS, .macOS, .macCatalyst, .watchOS])), 56 | ] 57 | ), 58 | .testTarget( 59 | name: "libPhoneNumberTests", 60 | dependencies: [ 61 | "libPhoneNumber", 62 | "libPhoneNumberTestsCommon", 63 | ], 64 | path: "libPhoneNumberTests" 65 | ), 66 | .target( 67 | name: "libPhoneNumberGeocodingMetaData", 68 | path: "libPhoneNumberGeocodingMetaData", 69 | resources: [ 70 | .copy("GeocodingMetaData.bundle") 71 | ], 72 | publicHeadersPath: "." 73 | ), 74 | .target( 75 | name: "libPhoneNumberGeocoding", 76 | dependencies: [ 77 | "libPhoneNumber", 78 | "libPhoneNumberGeocodingMetaData", 79 | ], 80 | path: "libPhoneNumberGeocoding", 81 | exclude: [ 82 | "README.md", 83 | "Info.plist", 84 | ], 85 | publicHeadersPath: "." 86 | ), 87 | .testTarget( 88 | name: "libPhoneNumberGeocodingTests", 89 | dependencies: [ 90 | "libPhoneNumberGeocoding", 91 | "libPhoneNumberTestsCommon", 92 | ], 93 | path: "libPhoneNumberGeocodingTests", 94 | resources: [ 95 | .copy("TestingSource.bundle") 96 | ] 97 | ), 98 | .target( 99 | name: "libPhoneNumberShortNumberInternal", 100 | dependencies: [ 101 | "libPhoneNumber", 102 | ], 103 | path: "libPhoneNumberShortNumberInternal", 104 | publicHeadersPath: "." 105 | ), 106 | .target( 107 | name: "libPhoneNumberShortNumber", 108 | dependencies: [ 109 | "libPhoneNumberShortNumberInternal", 110 | ], 111 | path: "libPhoneNumberShortNumber", 112 | exclude: [ 113 | "README.md", 114 | "Info.plist", 115 | ], 116 | publicHeadersPath: "." 117 | ), 118 | .testTarget( 119 | name: "libPhoneNumberShortNumberTests", 120 | dependencies: [ 121 | "libPhoneNumberShortNumber", 122 | "libPhoneNumberTestsCommon", 123 | ], 124 | path: "libPhoneNumberShortNumberTests" 125 | ), 126 | ] 127 | ) 128 | -------------------------------------------------------------------------------- /libPhoneNumber-Demo/libPhoneNumber-Demo.xcodeproj/xcshareddata/xcschemes/libPhoneNumber-Demo.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 9 | 10 | 16 | 22 | 23 | 24 | 25 | 26 | 32 | 33 | 35 | 41 | 42 | 43 | 45 | 51 | 52 | 53 | 54 | 55 | 65 | 67 | 73 | 74 | 75 | 76 | 82 | 84 | 90 | 91 | 92 | 93 | 95 | 96 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /libPhoneNumber/NBPhoneNumber.m: -------------------------------------------------------------------------------- 1 | // 2 | // NBPhoneNumber.m 3 | // libPhoneNumber 4 | // 5 | // 6 | 7 | #import "NBPhoneNumber.h" 8 | #import "NBPhoneNumberDefines.h" 9 | 10 | @implementation NBPhoneNumber 11 | 12 | - (instancetype)init { 13 | self = [super init]; 14 | 15 | if (self) { 16 | self.nationalNumber = @-1; 17 | self.countryCode = @-1; 18 | self.numberOfLeadingZeros = @(1); 19 | } 20 | 21 | return self; 22 | } 23 | 24 | - (void)clearCountryCodeSource { 25 | [self setCountryCodeSource:nil]; 26 | } 27 | 28 | - (NBECountryCodeSource)getCountryCodeSourceOrDefault { 29 | if (nil == self.countryCodeSource) { 30 | return NBECountryCodeSourceFROM_NUMBER_WITH_PLUS_SIGN; 31 | } 32 | 33 | return [self.countryCodeSource integerValue]; 34 | } 35 | 36 | - (NSUInteger)hash { 37 | // See https://stackoverflow.com/questions/4948780/magic-number-in-boosthash-combine 38 | NSUInteger hash = self.countryCode.hash; 39 | hash ^= self.nationalNumber.hash + 0x9e3779b9 + (hash << 6) + (hash >> 2); 40 | hash ^= self.numberOfLeadingZeros.hash + 0x9e3779b9 + (hash << 6) + (hash >> 2); 41 | hash ^= self.extension.hash + 0x9e3779b9 + (hash << 6) + (hash >> 2); 42 | return hash; 43 | } 44 | 45 | - (BOOL)isEqual:(id)object { 46 | if (![object isKindOfClass:[NBPhoneNumber class]]) { 47 | return NO; 48 | } 49 | 50 | NBPhoneNumber *other = object; 51 | return ([self.countryCode isEqualToNumber:other.countryCode]) && 52 | ([self.nationalNumber isEqualToNumber:other.nationalNumber]) && 53 | (self.italianLeadingZero == other.italianLeadingZero) && 54 | ([self.numberOfLeadingZeros isEqualToNumber:other.numberOfLeadingZeros]) && 55 | ((self.extension == nil && other.extension == nil) || 56 | [self.extension isEqualToString:other.extension]); 57 | } 58 | 59 | - (id)copyWithZone:(NSZone *)zone { 60 | NBPhoneNumber *phoneNumberCopy = [[NBPhoneNumber allocWithZone:zone] init]; 61 | 62 | phoneNumberCopy.countryCode = [self.countryCode copy]; 63 | phoneNumberCopy.nationalNumber = [self.nationalNumber copy]; 64 | phoneNumberCopy.extension = [self.extension copy]; 65 | phoneNumberCopy.italianLeadingZero = self.italianLeadingZero; 66 | phoneNumberCopy.numberOfLeadingZeros = [self.numberOfLeadingZeros copy]; 67 | phoneNumberCopy.rawInput = [self.rawInput copy]; 68 | phoneNumberCopy.countryCodeSource = [self.countryCodeSource copy]; 69 | phoneNumberCopy.preferredDomesticCarrierCode = [self.preferredDomesticCarrierCode copy]; 70 | 71 | return phoneNumberCopy; 72 | } 73 | 74 | - (instancetype)initWithCoder:(NSCoder *)coder { 75 | if (self = [super init]) { 76 | self.countryCode = [coder decodeObjectForKey:@"countryCode"]; 77 | self.nationalNumber = [coder decodeObjectForKey:@"nationalNumber"]; 78 | self.extension = [coder decodeObjectForKey:@"extension"]; 79 | self.italianLeadingZero = [[coder decodeObjectForKey:@"italianLeadingZero"] boolValue]; 80 | self.numberOfLeadingZeros = [coder decodeObjectForKey:@"numberOfLeadingZeros"]; 81 | self.rawInput = [coder decodeObjectForKey:@"rawInput"]; 82 | self.countryCodeSource = [coder decodeObjectForKey:@"countryCodeSource"]; 83 | self.preferredDomesticCarrierCode = [coder decodeObjectForKey:@"preferredDomesticCarrierCode"]; 84 | } 85 | return self; 86 | } 87 | 88 | - (void)encodeWithCoder:(NSCoder *)coder { 89 | [coder encodeObject:self.countryCode forKey:@"countryCode"]; 90 | [coder encodeObject:self.nationalNumber forKey:@"nationalNumber"]; 91 | [coder encodeObject:self.extension forKey:@"extension"]; 92 | [coder encodeObject:[NSNumber numberWithBool:self.italianLeadingZero] 93 | forKey:@"italianLeadingZero"]; 94 | [coder encodeObject:self.numberOfLeadingZeros forKey:@"numberOfLeadingZeros"]; 95 | [coder encodeObject:self.rawInput forKey:@"rawInput"]; 96 | [coder encodeObject:self.countryCodeSource forKey:@"countryCodeSource"]; 97 | [coder encodeObject:self.preferredDomesticCarrierCode forKey:@"preferredDomesticCarrierCode"]; 98 | } 99 | 100 | - (NSString *)description { 101 | return [NSString 102 | stringWithFormat:@" - countryCode[%@], nationalNumber[%@], extension[%@], " 103 | @"italianLeadingZero[%@], numberOfLeadingZeros[%@], rawInput[%@] " 104 | @"countryCodeSource[%@] preferredDomesticCarrierCode[%@]", 105 | self.countryCode, self.nationalNumber, self.extension, 106 | self.italianLeadingZero ? @"Y" : @"N", self.numberOfLeadingZeros, 107 | self.rawInput, self.countryCodeSource, self.preferredDomesticCarrierCode]; 108 | } 109 | 110 | @end 111 | -------------------------------------------------------------------------------- /libPhoneNumber-Demo/libPhoneNumber-Demo/GeocodingTableView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GeocodingTableView.swift 3 | // libPhoneNumber-Demo 4 | // 5 | // Created by Rastaar Haghi on 7/17/20. 6 | // Copyright © 2020 Google LLC. All rights reserved. 7 | // 8 | 9 | import SwiftUI 10 | import libPhoneNumberGeocoding 11 | 12 | #if canImport(libPhoneNumber) 13 | import libPhoneNumber 14 | #elseif canImport(libPhoneNumber_iOS) 15 | import libPhoneNumber_iOS 16 | #endif 17 | 18 | struct GeocodingTableView: View { 19 | // Keep track of runtime statistics 20 | var maxRuntime: CGFloat = 0.00 21 | var minRuntime: CGFloat = 0.00 22 | var totalRuntime: CGFloat = 0.00 23 | var averageRuntime: CGFloat = 0.00 24 | 25 | init() { 26 | for _ in 1...50 { 27 | makeGeocodingAPICalls() 28 | } 29 | 30 | self.maxRuntime = runtimeArray.max() ?? 0.0 31 | self.minRuntime = runtimeArray.min() ?? 0.0 32 | self.totalRuntime = 0.00 33 | for i in 0..<500 { 34 | totalRuntime += runtimeArray[i] 35 | runtimeArray[i] = runtimeArray[i] / maxRuntime 36 | } 37 | self.averageRuntime = totalRuntime / CGFloat(runtimeArray.count) 38 | } 39 | 40 | var body: some View { 41 | VStack { 42 | Form { 43 | Section(header: Text("This table makes 500 Geocoding API calls")) { 44 | List { 45 | ForEach(regionDescriptions, id: \.self) { pair in 46 | HStack { 47 | Text(pair[0]!) 48 | Spacer() 49 | Text(pair[1]!) 50 | } 51 | } 52 | } 53 | } 54 | } 55 | Form { 56 | Section(header: Text("Runtime Performance for Geocoding API Calls")) { 57 | LineGraph(dataPoints: runtimeArray) 58 | .stroke(Color.green, lineWidth: 2) 59 | .aspectRatio(16 / 9, contentMode: .fit) 60 | .border(Color.gray, width: 1) 61 | .padding() 62 | } 63 | Section(header: Text("Statistics for Runtime Performance (in milliseconds)")) { 64 | List { 65 | Text("Average API Call Runtime: \(round(averageRuntime).description)") 66 | Text("Longest API Call Runtime: \(round(maxRuntime).description)") 67 | Text("Shortest API Call Runtime: \(round(minRuntime).description)") 68 | Text("Total Runtime: \(round(totalRuntime).description)") 69 | } 70 | } 71 | } 72 | } 73 | .navigationBarTitle("Large Set of Geocoding Calls") 74 | } 75 | } 76 | 77 | struct GeocodingTableView_Previews: PreviewProvider { 78 | static var previews: some View { 79 | GeocodingTableView() 80 | } 81 | } 82 | 83 | extension GeocodingTableView { 84 | // Fetch Geocoding info for each number in phoneNumbers 85 | func makeGeocodingAPICalls() { 86 | for phoneNumber in phoneNumbers { 87 | do { 88 | let startTimer = DispatchTime.now() 89 | let parsedPhoneNumber = try NBPhoneNumberUtil.sharedInstance().parse(phoneNumber, defaultRegion: "US") 90 | regionDescriptions.append([ 91 | phoneNumber, 92 | geocoder.description(for: parsedPhoneNumber), 93 | ]) 94 | let endTimer = DispatchTime.now() 95 | let runtimeData = CGFloat(endTimer.uptimeNanoseconds - startTimer.uptimeNanoseconds) 96 | runtimeArray.append(runtimeData / 1000000.0) 97 | } catch { 98 | print(error) 99 | } 100 | } 101 | } 102 | 103 | // Graph Design based from: https://www.objc.io/blog/2020/03/16/swiftui-line-graph-animation/ 104 | struct LineGraph: Shape { 105 | var dataPoints: [CGFloat] 106 | 107 | func path(in rect: CGRect) -> Path { 108 | func point(at ix: Int) -> CGPoint { 109 | let point = dataPoints[ix] 110 | let x = rect.width * CGFloat(ix) / CGFloat(dataPoints.count - 1) 111 | let y = (1 - point) * rect.height 112 | return CGPoint(x: x, y: y) 113 | } 114 | 115 | return Path { p in 116 | guard dataPoints.count > 1 else { return } 117 | let start = dataPoints[0] 118 | p.move(to: CGPoint(x: 0, y: (1 - start) * rect.height)) 119 | for idx in dataPoints.indices { 120 | p.addLine(to: point(at: idx)) 121 | } 122 | } 123 | } 124 | } 125 | } 126 | 127 | let phoneNumbers: [String] = [ 128 | "19098611234", 129 | "14159601234", 130 | "12014321234", 131 | "12034811234", 132 | "12067061234", 133 | "12077711234", 134 | "12144681234", 135 | "12158231234", 136 | "12394351234", 137 | "12534591234", 138 | ] 139 | 140 | private var geocoder: NBPhoneNumberOfflineGeocoder = NBPhoneNumberOfflineGeocoder() 141 | var regionDescriptions: [[String?]] = [] 142 | var runtimeArray: [CGFloat] = [] 143 | -------------------------------------------------------------------------------- /libPhoneNumber-GeocodingParser/libPhoneNumber-GeocodingParser/NBSQLiteDatabaseConnection.m: -------------------------------------------------------------------------------- 1 | // 2 | // NBSQLiteDatabaseConnection.m 3 | // libPhoneNumber-GeocodingParser 4 | // 5 | // Created by Rastaar Haghi on 7/1/20. 6 | // Copyright © 2020 Google LLC. All rights reserved. 7 | // 8 | 9 | #import "NBSQLiteDatabaseConnection.h" 10 | 11 | @interface NBSQLiteDatabaseConnection() 12 | 13 | @property (nonatomic, strong) NSURL* databasePath; 14 | @property (nonatomic) sqlite3* DB; 15 | @property (nonatomic) sqlite3_stmt* insertStatement; 16 | @property (nonatomic) int sqliteDatabaseCode; 17 | 18 | @end 19 | 20 | @implementation NBSQLiteDatabaseConnection 21 | 22 | static NSString *const insertPreparedStatement = @"INSERT INTO geocodingPairs%@ " 23 | "(NATIONALNUMBER, DESCRIPTION)" 24 | "VALUES (?, ?)"; 25 | 26 | static NSString *const createTablePreparedStatement = 27 | @"CREATE TABLE IF NOT EXISTS geocodingPairs%@ (" 28 | "ID INTEGER PRIMARY KEY AUTOINCREMENT, " 29 | "NATIONALNUMBER TEXT, " 30 | "DESCRIPTION TEXT)"; 31 | static NSString *const createIndexStatement = @"CREATE INDEX IF NOT EXISTS nationalNumberIndex ON geocodingPairs%@(NATIONALNUMBER)"; 32 | 33 | - (instancetype)initWithCountryCode:(NSString *)countryCode 34 | withLanguage:(NSString *)language 35 | withDestinationPath:(NSURL *)destinationPath { 36 | self = [super init]; 37 | if (self != nil) { 38 | NSString *dbFileName = [NSString stringWithFormat:@"%@.db", language]; 39 | NSString *databasePath = [destinationPath.path stringByAppendingPathComponent:dbFileName]; 40 | self.sqliteDatabaseCode = sqlite3_open([databasePath UTF8String], &_DB); 41 | 42 | if (_sqliteDatabaseCode == SQLITE_OK) { 43 | [self createTable:countryCode]; 44 | const char *formattedPreparedStatement = [[NSString stringWithFormat:insertPreparedStatement, countryCode] UTF8String]; 45 | sqlite3_prepare_v2(self.DB, formattedPreparedStatement, -1, &_insertStatement, NULL); 46 | } else { 47 | NSLog(@"Cannot open database at desired location: %@\n" 48 | "SQLite3 Error Message: %s (%d)", 49 | destinationPath, sqlite3_errstr(self.sqliteDatabaseCode), self.sqliteDatabaseCode); 50 | } 51 | } 52 | return self; 53 | } 54 | 55 | - (void)addEntryToDB:(NSString *)phoneNumber 56 | withDescription:(NSString *)description 57 | withCountryCode:(NSString *)countryCode { 58 | if (self.sqliteDatabaseCode != SQLITE_OK) { 59 | NSLog(@"Cannot open database. Failed to add entry. \n" 60 | "SQLite3 Error Message: %s", 61 | sqlite3_errstr(self.sqliteDatabaseCode)); 62 | return; 63 | } 64 | int commandResults = [self createInsertStatement:phoneNumber withDescription:description]; 65 | if (commandResults != SQLITE_OK) { 66 | NSLog(@"Error when creating insert statement: %s", sqlite3_errstr(commandResults)); 67 | } 68 | sqlite3_step(self.insertStatement); 69 | } 70 | 71 | - (void)dealloc { 72 | sqlite3_finalize(self.insertStatement); 73 | sqlite3_close_v2(self.DB); 74 | } 75 | 76 | - (void)createTable:(NSString *)countryCode { 77 | NSString *createTable = [NSString stringWithFormat:createTablePreparedStatement, countryCode]; 78 | 79 | const char *sqliteCreateTableStatement = [createTable UTF8String]; 80 | char *sqliteErrorMessage; 81 | if (sqlite3_exec(self.DB, sqliteCreateTableStatement, NULL, NULL, &sqliteErrorMessage) != SQLITE_OK) { 82 | NSLog(@"Error creating table, %s", sqliteErrorMessage); 83 | return; 84 | } 85 | 86 | NSString *createIndexQuery = [NSString stringWithFormat:createIndexStatement, countryCode]; 87 | const char *sqlCreateIndexStatement = [createIndexQuery UTF8String]; 88 | if (sqlite3_exec(self.DB, sqlCreateIndexStatement, NULL, NULL, &sqliteErrorMessage) != SQLITE_OK) { 89 | NSLog(@"Error occurred when applying index to nationalnumber column: %s", sqliteErrorMessage); 90 | return; 91 | } 92 | } 93 | 94 | - (int)resetInsertStatement { 95 | // If an error occurs during statement reset, return error code. 96 | // Otherwise, return outcome of clearing bindings on the statement. 97 | int sqlResultCode = sqlite3_reset(self.insertStatement); 98 | if (sqlResultCode != SQLITE_OK) { 99 | return sqlResultCode; 100 | } else { 101 | return sqlite3_clear_bindings(self.insertStatement); 102 | } 103 | } 104 | 105 | - (int)createInsertStatement:(NSString *)phoneNumber withDescription:(NSString *)description { 106 | int sqliteResultCode = [self resetInsertStatement]; 107 | if (sqliteResultCode != SQLITE_OK) { 108 | NSLog(@"SQLite3 error occurred when resetting and clearing bindings in insert statement: %s", sqlite3_errstr(sqliteResultCode)); 109 | return sqliteResultCode; 110 | } else { 111 | sqlite3_bind_text(self.insertStatement, 1, [phoneNumber UTF8String], -1, SQLITE_TRANSIENT); 112 | sqlite3_bind_text(self.insertStatement, 2, [description UTF8String], -1, SQLITE_TRANSIENT); 113 | } 114 | return sqliteResultCode; 115 | } 116 | 117 | @end 118 | -------------------------------------------------------------------------------- /libPhoneNumber-Demo/libPhoneNumber-Demo-SPM.xcodeproj/xcshareddata/xcschemes/libPhoneNumber-Demo.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 9 | 10 | 16 | 22 | 23 | 24 | 25 | 26 | 32 | 33 | 35 | 41 | 42 | 43 | 45 | 51 | 52 | 53 | 54 | 55 | 65 | 67 | 73 | 74 | 75 | 76 | 82 | 83 | 84 | 85 | 91 | 93 | 99 | 100 | 101 | 102 | 108 | 109 | 110 | 111 | 113 | 114 | 117 | 118 | 119 | -------------------------------------------------------------------------------- /libPhoneNumberShortNumber/NBShortNumberMetadataHelper.m: -------------------------------------------------------------------------------- 1 | // 2 | // NBShortNumberMetadataHelper.m 3 | // libPhoneNumberShortNumber 4 | // 5 | // Created by Rastaar Haghi on 7/15/20. 6 | // Copyright © 2020 Google. All rights reserved. 7 | // 8 | 9 | #import "NBShortNumberMetadataHelper.h" 10 | #import "NBGeneratedShortNumberMetaData.h" 11 | #import "NBMetadataHelper.h" 12 | #import "NBPhoneMetaData.h" 13 | 14 | static NSString *StringByTrimming(NSString *aString) { 15 | static dispatch_once_t onceToken; 16 | static NSCharacterSet *whitespaceCharSet = nil; 17 | dispatch_once(&onceToken, ^{ 18 | NSMutableCharacterSet *spaceCharSet = 19 | [NSMutableCharacterSet characterSetWithCharactersInString:NB_NON_BREAKING_SPACE]; 20 | [spaceCharSet formUnionWithCharacterSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; 21 | whitespaceCharSet = spaceCharSet; 22 | }); 23 | return [aString stringByTrimmingCharactersInSet:whitespaceCharSet]; 24 | } 25 | 26 | @implementation NBShortNumberMetadataHelper { 27 | NSCache *_shortNumberMetadataCache; 28 | NBMetadataHelper *_helper; 29 | NSDictionary *_shortNumberDataMap; 30 | } 31 | 32 | - (instancetype)init { 33 | return [self initWithZippedDataBytes:kShortNumberMetaData 34 | compressedLength:kShortNumberMetaDataCompressedLength 35 | expandedLength:kShortNumberMetaDataExpandedLength 36 | metadataHelper:[[NBMetadataHelper alloc] init]]; 37 | } 38 | 39 | - (instancetype)initWithZippedData:(NSData *)data 40 | expandedLength:(NSUInteger)expandedLength 41 | metadataHelper:(NBMetadataHelper *)helper { 42 | return [self initWithZippedDataBytes:(z_const Bytef *)data.bytes 43 | compressedLength:data.length 44 | expandedLength:expandedLength 45 | metadataHelper:helper]; 46 | } 47 | 48 | - (instancetype)initWithZippedDataBytes:(z_const Bytef *)data 49 | compressedLength:(NSUInteger)compressedLength 50 | expandedLength:(NSUInteger)expandedLength 51 | metadataHelper:(NBMetadataHelper *)helper { 52 | self = [super init]; 53 | 54 | if (self != nil) { 55 | _helper = [[NBMetadataHelper alloc] init]; 56 | _shortNumberMetadataCache = [[NSCache alloc] init]; 57 | _shortNumberDataMap = 58 | [NBShortNumberMetadataHelper jsonObjectFromZippedDataWithBytes:data 59 | compressedLength:compressedLength 60 | expandedLength:expandedLength]; 61 | } 62 | 63 | return self; 64 | } 65 | 66 | - (NBPhoneMetaData * _Nullable)shortNumberMetadataForRegion:(NSString * _Nonnull)regionCode { 67 | regionCode = StringByTrimming(regionCode); 68 | if (regionCode.length == 0) { 69 | return nil; 70 | } 71 | regionCode = [regionCode uppercaseString]; 72 | 73 | NBPhoneMetaData *cachedMetadata = [_shortNumberMetadataCache objectForKey:regionCode]; 74 | if (cachedMetadata != nil) { 75 | return cachedMetadata; 76 | } 77 | 78 | NSDictionary *dict = _shortNumberDataMap[@"countryToMetadata"]; 79 | NSArray *entry = dict[regionCode]; 80 | if (entry) { 81 | NBPhoneMetaData *metadata = [[NBPhoneMetaData alloc] initWithEntry:entry]; 82 | [_shortNumberMetadataCache setObject:metadata forKey:regionCode]; 83 | return metadata; 84 | } 85 | 86 | return nil; 87 | } 88 | 89 | /** 90 | * Expand gzipped data into a JSON object. 91 | 92 | * @param bytes Array of zipped data. 93 | * @param compressedLength Length of the compressed bytes. 94 | * @param expandedLength Length of the expanded bytes. 95 | * @return JSON dictionary. 96 | */ 97 | + (NSDictionary *)jsonObjectFromZippedDataWithBytes:(z_const Bytef[])bytes 98 | compressedLength:(NSUInteger)compressedLength 99 | expandedLength:(NSUInteger)expandedLength { 100 | // Data is a gzipped JSON file that is embedded in the binary. 101 | // See GeneratePhoneNumberHeader.sh and PhoneNumberMetaData.h for details. 102 | NSMutableData *gunzippedData = [NSMutableData dataWithLength:expandedLength]; 103 | 104 | z_stream zStream; 105 | memset(&zStream, 0, sizeof(zStream)); 106 | __attribute((unused)) int err = inflateInit2(&zStream, 16); 107 | NSAssert(err == Z_OK, @"Unable to init stream. err = %d", err); 108 | 109 | zStream.next_in = bytes; 110 | zStream.avail_in = (uint)compressedLength; 111 | zStream.next_out = (Bytef *)gunzippedData.bytes; 112 | zStream.avail_out = (uint)gunzippedData.length; 113 | 114 | err = inflate(&zStream, Z_FINISH); 115 | NSAssert(err == Z_STREAM_END, @"Unable to inflate compressed data. err = %d", err); 116 | 117 | err = inflateEnd(&zStream); 118 | NSAssert(err == Z_OK, @"Unable to inflate compressed data. err = %d", err); 119 | 120 | NSError *error = nil; 121 | NSDictionary *jsonObject = [NSJSONSerialization JSONObjectWithData:gunzippedData 122 | options:0 123 | error:&error]; 124 | NSAssert(error == nil, @"Unable to convert JSON - %@", error); 125 | 126 | return jsonObject; 127 | } 128 | 129 | @end 130 | -------------------------------------------------------------------------------- /libPhoneNumberGeocoding/NBGeocoderMetaDataHelper.m: -------------------------------------------------------------------------------- 1 | // 2 | // NBGeocoderMetaDataHelper.m 3 | // libPhoneNumberiOS 4 | // 5 | // Created by Rastaar Haghi on 6/12/20. 6 | // Copyright © 2020 Google LLC. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "NBGeocoderMetaDataHelper.h" 12 | #import "NBPhoneNumber.h" 13 | 14 | @implementation NBGeocoderMetaDataHelper { 15 | @private 16 | sqlite3 *_database; 17 | sqlite3_stmt *_selectStatement; 18 | NSString *_language; 19 | NSNumber *_countryCode; 20 | const char *_completePhoneNumber; 21 | } 22 | 23 | static NSString *const preparedStatement = @"WITH recursive count(x)" 24 | @"AS" 25 | @"( " 26 | @"SELECT 1 " 27 | @"UNION ALL " 28 | @"SELECT x+1 " 29 | @"FROM count " 30 | @"LIMIT length(?)), tosearch " 31 | @"AS " 32 | @"( " 33 | @"SELECT substr(?, 1, x) AS indata " 34 | @"FROM count) " 35 | @"SELECT nationalnumber, " 36 | @"description, " 37 | @"length(nationalnumber) AS nationalnumberlength " 38 | @"FROM geocodingpairs%@ " 39 | @"WHERE nationalnumber IN tosearch " 40 | @"ORDER BY nationalnumberlength DESC " 41 | @"LIMIT 2"; 42 | 43 | - (instancetype)initWithCountryCode:(NSNumber *)countryCode 44 | withLanguage:(NSString *)languageCode 45 | withBundle:(NSBundle *)bundle { 46 | self = [super init]; 47 | if (self != nil) { 48 | _countryCode = countryCode; 49 | _language = languageCode; 50 | 51 | NSURL *bundleURL = [bundle resourceURL]; 52 | NSString *shortLanguageCode = [[languageCode componentsSeparatedByString:@"-"] firstObject]; 53 | NSString *databasePath = [NSString stringWithFormat:@"%@%@.db", bundleURL, shortLanguageCode]; 54 | if (databasePath == nil) { 55 | @throw [NSException exceptionWithName:NSInvalidArgumentException 56 | reason:@"Geocoding Database URL not found" 57 | userInfo:nil]; 58 | } 59 | sqlite3_open([databasePath UTF8String], &_database); 60 | 61 | sqlite3_prepare_v2(_database, 62 | [[NSString stringWithFormat:preparedStatement, countryCode] UTF8String], -1, 63 | &_selectStatement, NULL); 64 | } 65 | return self; 66 | } 67 | 68 | - (instancetype)initWithCountryCode:(NSNumber *)countryCode withLanguage:(NSString *)languageCode { 69 | NSBundle *bundle = [NSBundle bundleForClass:self.classForCoder]; 70 | NSURL *resourceURL = 71 | [[bundle resourceURL] URLByAppendingPathComponent:@"GeocodingMetaData.bundle"]; 72 | NSBundle *databaseBundle = [NSBundle bundleWithURL:resourceURL]; 73 | return [self initWithCountryCode:countryCode withLanguage:languageCode withBundle:databaseBundle]; 74 | } 75 | 76 | - (NSString * _Nullable)searchPhoneNumber:(NBPhoneNumber *)phoneNumber { 77 | @synchronized(self) { 78 | if (![phoneNumber.countryCode isEqualToNumber:_countryCode]) { 79 | _countryCode = phoneNumber.countryCode; 80 | sqlite3_finalize(_selectStatement); 81 | sqlite3_prepare_v2(_database, 82 | [[NSString stringWithFormat:preparedStatement, _countryCode] UTF8String], 83 | -1, &_selectStatement, NULL); 84 | } 85 | 86 | int sqlCommandResults = [self createSelectStatement:phoneNumber]; 87 | 88 | if (sqlCommandResults != SQLITE_OK) { 89 | NSLog(@"Error with preparing statement. SQLite3 error code was: %d", sqlCommandResults); 90 | return nil; 91 | } 92 | int step = sqlite3_step(_selectStatement); 93 | if (step == SQLITE_ROW) { 94 | return @((const char *)sqlite3_column_text(_selectStatement, 1)); 95 | } else { 96 | return nil; 97 | } 98 | } 99 | } 100 | 101 | - (void)dealloc { 102 | sqlite3_finalize(_selectStatement); 103 | sqlite3_close_v2(_database); 104 | } 105 | 106 | - (int)resetSelectStatement { 107 | sqlite3_reset(_selectStatement); 108 | return sqlite3_clear_bindings(_selectStatement); 109 | } 110 | 111 | - (const char *)createCompletePhoneNumber:(NBPhoneNumber *)phoneNumber { 112 | if ([phoneNumber italianLeadingZero]) { 113 | return [[NSString 114 | stringWithFormat:@"%@0%@", phoneNumber.countryCode, phoneNumber.nationalNumber] UTF8String]; 115 | } else { 116 | return [[NSString stringWithFormat:@"%@%@", phoneNumber.countryCode, phoneNumber.nationalNumber] 117 | UTF8String]; 118 | } 119 | } 120 | 121 | - (void)bindPhoneNumberToSelectStatement { 122 | sqlite3_bind_text(_selectStatement, 1, _completePhoneNumber, -1, SQLITE_TRANSIENT); 123 | sqlite3_bind_text(_selectStatement, 2, _completePhoneNumber, -1, SQLITE_TRANSIENT); 124 | } 125 | 126 | - (int)createSelectStatement:(NBPhoneNumber *)phoneNumber { 127 | int sqliteResultCode; 128 | @autoreleasepool { 129 | sqliteResultCode = [self resetSelectStatement]; 130 | if (sqliteResultCode == SQLITE_OK) { 131 | _completePhoneNumber = [self createCompletePhoneNumber:phoneNumber]; 132 | [self bindPhoneNumberToSelectStatement]; 133 | } else { 134 | NSLog(@"SQLite3 error occurred when resetting and clearing bindings in select statement: %s", 135 | sqlite3_errstr(sqliteResultCode)); 136 | } 137 | } 138 | return sqliteResultCode; 139 | } 140 | 141 | @end 142 | -------------------------------------------------------------------------------- /scripts/GeneratePhoneNumberMetaDataFiles.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # GeneratePhoneNumberMetaDataFiles.sh 4 | # libPhoneNumber 5 | # 6 | # Created by Dave MacLachlan on 2/7/17. 7 | # Copyright © 2017 Google Inc. All rights reserved. 8 | 9 | # Takes the data sets in the PhoneNumberMetaData files and compresses them and then 10 | # writes them into headers that we can pull into our source. The compression reduces them from 300k 11 | # of data to 44k (per architecture). It would possibly be better to have this as a resource file 12 | # that is read in from disk (because then we only pay for the size once), but that would 13 | # dramatically change how this library is currently used by clients. 14 | # The testing data is zipped into a resource file. 15 | 16 | set -eu 17 | 18 | cd "${BASH_SOURCE%/*}" || exit 19 | 20 | echo "----------------------------------------------" 21 | 22 | readonly CORE_METADATA_HEADER="../libPhoneNumberInternal/NBGeneratedPhoneNumberMetaData.h" 23 | readonly CORE_METADATA_IMPL="../libPhoneNumber/NBGeneratedPhoneNumberMetaData.m" 24 | readonly SHORT_NUMBER_METADATA_HEADER="../libPhoneNumberShortNumberInternal/NBGeneratedShortNumberMetaData.h" 25 | readonly SHORT_NUMBER_METADATA_IMPL="../libPhoneNumberShortNumber/NBGeneratedShortNumberMetaData.m" 26 | 27 | readonly TEST_METADATA_ZIP="../libPhoneNumberTestsCommon/libPhoneNumberMetaDataForTesting.zip" 28 | readonly TEST_METADATA_HEADER_NAME="NBTestingMetaData.h" 29 | readonly TEST_METADATA_HEADER="../libPhoneNumberTestsCommon/$TEST_METADATA_HEADER_NAME" 30 | readonly TEST_METADATA_IMPL="../libPhoneNumberTestsCommon/NBTestingMetaData.m" 31 | 32 | 33 | echo "Compressing JSON Files..." 34 | 35 | TEMPDIR=$(mktemp -d) 36 | gzip -c "../generatedJSON/PhoneNumberMetaData.json" > "$TEMPDIR/PhoneNumberMetaData.zip" 37 | gzip -c "../generatedJSON/ShortNumberMetaData.json" > "$TEMPDIR/ShortNumberMetaData.zip" 38 | 39 | 40 | echo "Generating Files..." 41 | 42 | echo " $CORE_METADATA_HEADER" 43 | 44 | # Core MetaData Header 45 | cat > "$CORE_METADATA_HEADER" <<'EOF' 46 | /***** 47 | * Data Generated from GeneratePhoneNumberHeader.sh 48 | * Off of PhoneNumberMetaData.json 49 | */ 50 | 51 | #include 52 | 53 | // z_const is not defined in some versions of zlib, so define it here 54 | // in case it has not been defined. 55 | #if defined(ZLIB_CONST) && !defined(z_const) 56 | # define z_const const 57 | #else 58 | # define z_const 59 | #endif 60 | 61 | extern z_const Bytef kPhoneNumberMetaData[]; 62 | extern z_const size_t kPhoneNumberMetaDataCompressedLength; 63 | extern z_const size_t kPhoneNumberMetaDataExpandedLength; 64 | EOF 65 | 66 | echo " $CORE_METADATA_IMPL" 67 | 68 | # Core MetaData Implementation 69 | cat > "$CORE_METADATA_IMPL" <<'EOF' 70 | #include "NBGeneratedPhoneNumberMetaData.h" 71 | 72 | z_const Bytef kPhoneNumberMetaData[] = { 73 | EOF 74 | 75 | cat "$TEMPDIR/PhoneNumberMetaData.zip" | xxd -i >> "$CORE_METADATA_IMPL" 76 | 77 | cat >> "$CORE_METADATA_IMPL" <<'EOF' 78 | }; 79 | z_const size_t kPhoneNumberMetaDataCompressedLength = sizeof(kPhoneNumberMetaData); 80 | EOF 81 | 82 | LIB_SIZE=$(stat -f%z "../generatedJSON/PhoneNumberMetaData.json") 83 | echo "z_const size_t kPhoneNumberMetaDataExpandedLength = $LIB_SIZE;" >> "$CORE_METADATA_IMPL" 84 | 85 | echo " $SHORT_NUMBER_METADATA_HEADER" 86 | 87 | # Short Number MetaData Header 88 | cat > "$SHORT_NUMBER_METADATA_HEADER" <<'EOF' 89 | /***** 90 | * Data Generated from GeneratePhoneNumberHeader.sh 91 | * Off of ShortNumberMetaData.json 92 | */ 93 | 94 | #include 95 | 96 | // z_const is not defined in some versions of zlib, so define it here 97 | // in case it has not been defined. 98 | #if defined(ZLIB_CONST) && !defined(z_const) 99 | # define z_const const 100 | #else 101 | # define z_const 102 | #endif 103 | 104 | extern z_const Bytef kShortNumberMetaData[]; 105 | extern z_const size_t kShortNumberMetaDataCompressedLength; 106 | extern z_const size_t kShortNumberMetaDataExpandedLength; 107 | EOF 108 | 109 | echo " $SHORT_NUMBER_METADATA_IMPL" 110 | 111 | # Short Number MetaData Implementation 112 | cat > "$SHORT_NUMBER_METADATA_IMPL" <<'EOF' 113 | #include "NBGeneratedShortNumberMetaData.h" 114 | 115 | z_const Bytef kShortNumberMetaData[] = { 116 | EOF 117 | 118 | cat "$TEMPDIR/ShortNumberMetaData.zip" | xxd -i >> "$SHORT_NUMBER_METADATA_IMPL" 119 | 120 | cat >> "$SHORT_NUMBER_METADATA_IMPL" <<'EOF' 121 | }; 122 | z_const size_t kShortNumberMetaDataCompressedLength = sizeof(kShortNumberMetaData); 123 | EOF 124 | 125 | LIB_SIZE=$(stat -f%z "../generatedJSON/ShortNumberMetaData.json") 126 | echo "z_const size_t kShortNumberMetaDataExpandedLength = $LIB_SIZE;" >> "$SHORT_NUMBER_METADATA_IMPL" 127 | 128 | 129 | echo "Compressiong Testing JSON..." 130 | 131 | echo " $TEST_METADATA_ZIP" 132 | 133 | # MetaData for testing 134 | gzip -c "../generatedJSON/PhoneNumberMetaDataForTesting.json" > "$TEST_METADATA_ZIP" 135 | 136 | 137 | echo "Generating Common Testing Files..." 138 | 139 | echo " $TEST_METADATA_HEADER" 140 | 141 | cat > "$TEST_METADATA_HEADER" <<'EOF' 142 | /***** 143 | * Data Generated from GeneratePhoneNumberHeader.sh 144 | * Off of PhoneNumberMetaDataForTesting.json 145 | */ 146 | 147 | #include 148 | 149 | // z_const is not defined in some versions of zlib, so define it here 150 | // in case it has not been defined. 151 | #if defined(ZLIB_CONST) && !defined(z_const) 152 | # define z_const const 153 | #else 154 | # define z_const 155 | #endif 156 | 157 | extern z_const size_t kPhoneNumberMetaDataForTestingExpandedLength; 158 | EOF 159 | 160 | echo " $TEST_METADATA_IMPL" 161 | 162 | LIB_SIZE=$(stat -f%z "../generatedJSON/PhoneNumberMetaDataForTesting.json") 163 | echo "#include \"$TEST_METADATA_HEADER_NAME\"\n" > "$TEST_METADATA_IMPL" 164 | echo "z_const size_t kPhoneNumberMetaDataForTestingExpandedLength = $LIB_SIZE;" >> "$TEST_METADATA_IMPL" 165 | 166 | 167 | echo "Cleaning up temporary files" 168 | 169 | rm "$TEMPDIR/PhoneNumberMetaData.zip" 170 | rm "$TEMPDIR/ShortNumberMetaData.zip" 171 | rmdir "$TEMPDIR" 172 | 173 | 174 | echo 175 | echo "Script completed successfully" 176 | 177 | echo "----------------------------------------------" 178 | -------------------------------------------------------------------------------- /libPhoneNumber-GeocodingParser/libPhoneNumber-GeocodingParser/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // libPhoneNumber-GeocodingParser 4 | // 5 | // Created by Rastaar Haghi on 7/1/20. 6 | // Copyright © 2020 Google LLC. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "SSZipArchive.h" 11 | 12 | #import "NBGeocoderMetadataParser.h" 13 | #import "ArgumentParser.h" 14 | 15 | int main(int argc, const char *argv[]) { 16 | @autoreleasepool { 17 | ArgResult* argResult = parseArguments(argc, argv); 18 | if (argResult == nil) { 19 | NSLog(@"The libPhoneNumber-iOS Geocoder Parser requires 2 input arguments to properly function.\n" 20 | "1. The version of metadata to download from the libphonenumber repo\n" 21 | "2. The complete directory path to the desired location to store the corresponding SQLite databases created.\n" 22 | "Example argument: /Users/JohnDoe/Desktop/geocoding/"); 23 | return 1; 24 | } 25 | 26 | // Download zip file from GitHub. 27 | 28 | NSString* branch = argResult->version; 29 | NSString* archiveVersion = branch; 30 | if(![branch isEqualToString:@"master"]) { 31 | archiveVersion = [NSString stringWithFormat:@"v%@", branch]; 32 | } 33 | 34 | NSString *repositoryString = [NSString stringWithFormat:@"https://github.com/google/libphonenumber/archive/%@.zip", archiveVersion]; 35 | 36 | NSURL *libPhoneNumberRepoURL = [NSURL URLWithString:repositoryString]; 37 | NSURL *databaseDesiredLocation = [NSURL fileURLWithPath:argResult->directory]; 38 | 39 | NSLog(@"Downloading geocoding metadata files from %@", repositoryString); 40 | NSData *libPhoneNumberData = [NSData dataWithContentsOfURL:libPhoneNumberRepoURL]; 41 | if(libPhoneNumberData==nil) { 42 | NSLog(@"Failed to download data from: %@", repositoryString); 43 | return 1; 44 | } 45 | 46 | NSLog(@"Downloaded geocoding metadata files."); 47 | 48 | // Gather the documents directory path 49 | NSString *temporaryDirectoryPath = NSTemporaryDirectory(); 50 | NSString *zipFilePath = [temporaryDirectoryPath stringByAppendingPathComponent:@"libPhoneNumber-iOS.zip"]; 51 | zipFilePath = [zipFilePath stringByStandardizingPath]; 52 | if(![libPhoneNumberData writeToFile:zipFilePath atomically:YES]) { 53 | NSLog(@"Failed to write to file: %@", zipFilePath); 54 | return 1; 55 | } 56 | 57 | NSLog(@"Attempting to unzip downloaded metadata."); 58 | 59 | // Unzip libphonenumber-master project using SSZipArchive library. 60 | [SSZipArchive unzipFileAtPath:zipFilePath toDestination:temporaryDirectoryPath]; 61 | NSLog(@"Successfully unzipped metadata files."); 62 | 63 | // Navigate through project to geocoding metadata resource files. 64 | NSString *libPhoneNumberFolderPath = [temporaryDirectoryPath stringByAppendingPathComponent:[NSString stringWithFormat:@"libphonenumber-%@", branch]]; 65 | NSError *error; 66 | NSArray *filesInDirectory = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:libPhoneNumberFolderPath error:&error]; 67 | if (error != nil) { 68 | NSLog(@"An error occurred when attempting to access files in repository. Error message: %@", error.description); 69 | return 1; 70 | } 71 | 72 | if (![filesInDirectory containsObject:@"resources"]) { 73 | NSLog(@"No 'resources' directory found"); 74 | return 1; 75 | } 76 | 77 | NSString *resourcesFolderPath = [libPhoneNumberFolderPath stringByAppendingPathComponent:@"resources"]; 78 | filesInDirectory = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:resourcesFolderPath error:&error]; 79 | if (error != NULL) { 80 | NSLog(@"An error occurred when attempting to access files in project's 'resources' folder. Error message: %@", error.description); 81 | return 1; 82 | } 83 | 84 | if (![filesInDirectory containsObject:@"geocoding"]) { 85 | NSLog(@"No 'geocoding' directory found"); 86 | return 1; 87 | } 88 | 89 | NSString *geocodingFolderPath = [resourcesFolderPath stringByAppendingPathComponent:@"geocoding"]; 90 | NSArray *languages = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:geocodingFolderPath error:&error]; 91 | if (error != NULL) { 92 | NSLog(@"An error occurred when attempting to access files in project's 'geocoding' folder. Error message: %@", error.description); 93 | return 1; 94 | } 95 | 96 | NBGeocoderMetadataParser *metadataParser = [[NBGeocoderMetadataParser alloc] initWithDestinationPath:[databaseDesiredLocation copy]]; 97 | 98 | NSLog(@"Saving language databases to folder: %@", databaseDesiredLocation.path); 99 | 100 | NSArray *textFilesAvailable; 101 | NSString *languageFolderPath; 102 | for (NSString *language in languages) { 103 | NSLog(@"Creating SQLite database file for the language: %@", language); 104 | languageFolderPath = [geocodingFolderPath stringByAppendingPathComponent:language]; 105 | textFilesAvailable = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:languageFolderPath error:&error]; 106 | if (error != NULL) { 107 | NSLog(@"Error occurred when trying to read files for the language directory: %@", languageFolderPath); 108 | error = NULL; 109 | continue; 110 | } 111 | 112 | for (NSString *filename in textFilesAvailable) { 113 | NSString *extension = [[filename pathExtension] lowercaseString]; 114 | if ([extension isEqualToString:@"txt"]) { 115 | NSString *completeFilePath = [languageFolderPath stringByAppendingPathComponent:filename]; 116 | [metadataParser convertFileToSQLiteDatabase:completeFilePath 117 | withFileName:filename 118 | withLanguage:language 119 | loggingIndent:@" "]; 120 | } 121 | } 122 | 123 | NSLog(@" Created SQLite database file for the language: %@", language); 124 | } 125 | } 126 | 127 | NSLog(@"\nGeocoding data updated successfully!\n\n"); 128 | return 0; 129 | } 130 | -------------------------------------------------------------------------------- /scripts/updateProjectVersions.swift: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env swift 2 | 3 | // 4 | // updateProjectVersions.swift 5 | // libPhoneNumber 6 | // 7 | // Created by Kris Kline on 12/3/25. 8 | // Copyright © 2025. All rights reserved. 9 | // 10 | 11 | import Foundation 12 | 13 | // MARK: - Script Configuration 14 | 15 | let scriptVersion = "1.0.0" 16 | 17 | let scriptName = URL(fileURLWithPath: (CommandLine.arguments.first)!).lastPathComponent 18 | 19 | let dividingLine = "-----------------------------------------------------" 20 | let header = 21 | """ 22 | \(dividingLine) 23 | \(scriptName) v\(scriptVersion) 24 | """ 25 | let footer = 26 | """ 27 | \(dividingLine) 28 | """ 29 | 30 | // MARK: - String Extension 31 | 32 | extension String { 33 | /// Whether this string represents a numbered version: X.Y.Z, X.Y, or Z 34 | var isVersion: Bool { 35 | // Regex to match a common version format (e.g., X.Y.Z, X.Y, X) 36 | // where X, Y, Z are one or more digits. 37 | let versionPattern = #"^\d+(\.\d+){0,2}$"# 38 | 39 | do { 40 | let regex = try Regex(versionPattern) 41 | return self.wholeMatch(of: regex) != nil 42 | } catch { 43 | return false 44 | } 45 | } 46 | } 47 | 48 | // MARK: - Argument Parsing 49 | 50 | enum LogMode { 51 | case verbose 52 | case quiet 53 | } 54 | 55 | var mode: LogMode = .verbose 56 | var newVersion: String? 57 | 58 | var argIter: IndexingIterator.SubSequence> = CommandLine.arguments.dropFirst().makeIterator() 59 | 60 | while let arg: String = argIter.next() { 61 | switch arg { 62 | case "-n", "--no-status": 63 | mode = .quiet 64 | default: 65 | if newVersion == nil, arg.isVersion { 66 | newVersion = arg 67 | } else { 68 | print("Unknown argument: \(arg)") 69 | exit(1) 70 | } 71 | } 72 | } 73 | 74 | guard let ver = newVersion else { 75 | print("Usage: \(scriptName) [-n|--no-status] ") 76 | exit(1) 77 | } 78 | 79 | /** 80 | Logs out the specified status message, if logging is enabled 81 | 82 | - parameter message: The status message to log out 83 | */ 84 | func logStatus(_ message: String) { 85 | guard mode == .verbose else { 86 | return 87 | } 88 | 89 | print(message) 90 | } 91 | 92 | // MARK: - File Search Utilities 93 | 94 | /** 95 | Finds all the files in the directory (and any subdirectories) with the specified suffixes 96 | 97 | - parameter suffixes: The file suffixes to look for 98 | - parameter directory: The directory to search in 99 | 100 | - returns The array of files with the specified suffixes 101 | */ 102 | func findFiles(suffixes: [String], in directory: URL) -> [URL] { 103 | let fm = FileManager.default 104 | var found = [URL]() 105 | let resourceKeys = [URLResourceKey.isDirectoryKey] 106 | let enumerator = fm.enumerator(at: directory, includingPropertiesForKeys: resourceKeys, options: [.skipsHiddenFiles])! 107 | for case let fileURL as URL in enumerator { 108 | // Ignore files within the cocoapods sub-project 109 | if fileURL.pathComponents.contains("Pods") { 110 | continue 111 | } 112 | 113 | let ext = fileURL.pathExtension.lowercased() 114 | let name = fileURL.lastPathComponent.lowercased() 115 | if suffixes.contains(ext) { 116 | found.append(fileURL) 117 | } else if suffixes.contains(where: { name.hasSuffix($0) }) { 118 | found.append(fileURL) 119 | } 120 | } 121 | return found 122 | } 123 | 124 | // MARK: - Version Updating 125 | 126 | /** 127 | Updates the specified pbxproj file to use the specfiied project version 128 | 129 | - parameter url: The location of the pbxproj file to update 130 | - parameter version: The new project version to use in the file 131 | 132 | - returns Whether the file was modified 133 | */ 134 | @discardableResult 135 | func updatePBXProj(at url: URL, toVersion version: String) -> Bool { 136 | guard let text = try? String(contentsOf: url, encoding: .utf8) else { 137 | return false 138 | } 139 | var modified = text 140 | let pattern = #"\s*=\s*[^;]+;"# 141 | let variableNames = ["MARKETING_VERSION", "CURRENT_PROJECT_VERSION"] 142 | var changed = false 143 | 144 | for name in variableNames { 145 | guard let regex = try? NSRegularExpression(pattern: name + pattern) else { continue } 146 | let results = regex.matches(in: modified, range: NSRange(modified.startIndex..., in: modified)) 147 | if !results.isEmpty { 148 | modified = regex.stringByReplacingMatches(in: modified, range: NSRange(modified.startIndex..., in: modified), withTemplate: "\(name) = \(version);") 149 | changed = true 150 | } 151 | } 152 | 153 | if changed, modified != text { 154 | try? modified.write(to: url, atomically: true, encoding: .utf8) 155 | return true 156 | } 157 | return false 158 | } 159 | 160 | /** 161 | Updates the `version` field in a `.podspec` file at the specified URL to the given version string. 162 | 163 | - parameter url: The file URL pointing to the `.podspec` file to update. 164 | - parameter version: The new version string to set for the `*.version` field. 165 | 166 | - returns Whether the file was modified 167 | */ 168 | @discardableResult 169 | func updatePodspec(at url: URL, toVersion version: String) -> Bool { 170 | guard let text = try? String(contentsOf: url, encoding: .utf8) else { 171 | return false 172 | } 173 | 174 | let podspecVersionPattern = #"(^\s*.*\.version\s*=\s*['\"])([^'\"]+)(['\"])"# 175 | let regex = try! NSRegularExpression(pattern: podspecVersionPattern, options: [.anchorsMatchLines]) 176 | 177 | let newText = regex.stringByReplacingMatches(in: text, range: NSRange(text.startIndex..., in: text), withTemplate: "$1\(version)$3") 178 | 179 | if text != newText { 180 | try? newText.write(to: url, atomically: true, encoding: .utf8) 181 | return true 182 | } 183 | return false 184 | } 185 | 186 | // MARK: - Script Execution 187 | 188 | let repoRoot = URL(fileURLWithPath: FileManager.default.currentDirectoryPath) 189 | var modifiedFiles = [String]() 190 | 191 | logStatus(header + "\n") 192 | 193 | let pbxprojFiles = findFiles(suffixes: ["pbxproj"], in: repoRoot) 194 | let podspecFiles = findFiles(suffixes: ["podspec"], in: repoRoot) 195 | 196 | logStatus("Found \(pbxprojFiles.count) *.pbxproj files") 197 | logStatus("Found \(podspecFiles.count) *.podspec files") 198 | 199 | logStatus("\nUpdating versions to: \(ver)\n") 200 | 201 | for file in pbxprojFiles { 202 | if updatePBXProj(at: file, toVersion: ver) { 203 | let relPath = file.path.replacingOccurrences(of: repoRoot.path + "/", with: "") 204 | modifiedFiles.append(relPath) 205 | logStatus("[UPDATED] \(relPath)") 206 | } 207 | } 208 | 209 | for file in podspecFiles { 210 | if updatePodspec(at: file, toVersion: ver) { 211 | let relPath = file.path.replacingOccurrences(of: repoRoot.path + "/", with: "") 212 | modifiedFiles.append(relPath) 213 | logStatus("[UPDATED] \(relPath)") 214 | } 215 | } 216 | 217 | logStatus("\nModified \(modifiedFiles.count) file(s):") 218 | 219 | if mode == .verbose { 220 | for path in modifiedFiles { print(path) } 221 | } else { 222 | modifiedFiles.forEach { print($0) } 223 | } 224 | 225 | logStatus(footer) 226 | -------------------------------------------------------------------------------- /libPhoneNumberGeocoding/NBPhoneNumberOfflineGeocoder.m: -------------------------------------------------------------------------------- 1 | // 2 | // NBPhoneNumberOfflineGeocoder.m 3 | // libPhoneNumberiOS 4 | // 5 | // Created by Rastaar Haghi on 6/12/20. 6 | // Copyright © 2020 Google LLC. All rights reserved. 7 | // 8 | 9 | #import "NBGeocoderMetaDataHelper.h" 10 | #import "NBPhoneNumberOfflineGeocoder.h" 11 | #import "NBPhoneNumber.h" 12 | #import "NBPhoneNumberUtil.h" 13 | 14 | @implementation NBPhoneNumberOfflineGeocoder { 15 | @private 16 | NBPhoneNumberUtil *_phoneNumberUtil; 17 | NSCache *_metadataHelpers; 18 | NBGeocoderMetaDataHelperFactory _metadataHelperFactory; 19 | } 20 | 21 | static NSString *const INVALID_REGION_CODE = @"ZZ"; 22 | 23 | - (instancetype)init { 24 | return [self 25 | initWithMetaDataHelperFactory:^NBGeocoderMetaDataHelper *(NSNumber *_Nonnull countryCode, 26 | NSString *_Nonnull language) { 27 | return [[NBGeocoderMetaDataHelper alloc] initWithCountryCode:countryCode 28 | withLanguage:language]; 29 | } 30 | phoneNumberUtil:NBPhoneNumberUtil.sharedInstance]; 31 | } 32 | 33 | - (instancetype)initWithMetaDataHelperFactory:(NBGeocoderMetaDataHelperFactory)factory 34 | phoneNumberUtil:(NBPhoneNumberUtil *)phoneNumberUtil { 35 | self = [super init]; 36 | if (self != nil) { 37 | _phoneNumberUtil = phoneNumberUtil; 38 | _metadataHelpers = [[NSCache alloc] init]; 39 | _metadataHelperFactory = [factory copy]; 40 | } 41 | return self; 42 | } 43 | 44 | + (NBPhoneNumberOfflineGeocoder *)sharedInstance { 45 | static dispatch_once_t onceToken; 46 | static NBPhoneNumberOfflineGeocoder *instance; 47 | dispatch_once(&onceToken, ^{ 48 | instance = [[self alloc] init]; 49 | }); 50 | return instance; 51 | } 52 | 53 | - (nullable NSString *)descriptionForValidNumber:(NBPhoneNumber *)phoneNumber 54 | withLanguageCode:(NSString *)languageCode { 55 | // If the NSCache doesn't contain a key equivalent to languageCode, create a 56 | // new NBGeocoderMetadataHelper object with a language set equal to 57 | // languageCode and default country code to United States / Canada 58 | if ([_metadataHelpers objectForKey:languageCode] == nil) { 59 | [_metadataHelpers setObject:_metadataHelperFactory(phoneNumber.countryCode, languageCode) 60 | forKey:languageCode]; 61 | } 62 | NSString *result = [[_metadataHelpers objectForKey:languageCode] searchPhoneNumber:phoneNumber]; 63 | if (result == nil) { 64 | return [self countryNameForNumber:phoneNumber withLanguageCode:languageCode]; 65 | } else { 66 | return result; 67 | } 68 | } 69 | 70 | - (nullable NSString *)descriptionForValidNumber:(NBPhoneNumber *)phoneNumber 71 | withLanguageCode:(NSString *)languageCode 72 | withUserRegion:(NSString *)userRegion { 73 | NSString *regionCode = [_phoneNumberUtil getRegionCodeForNumber:phoneNumber]; 74 | if ([userRegion isEqualToString:regionCode]) { 75 | return [self descriptionForValidNumber:phoneNumber withLanguageCode:languageCode]; 76 | } 77 | return [self regionDisplayName:regionCode withLanguageCode:languageCode]; 78 | } 79 | 80 | - (nullable NSString *)descriptionForNumber:(NBPhoneNumber *)phoneNumber 81 | withLanguageCode:(NSString *)languageCode { 82 | NBEPhoneNumberType numberType = [_phoneNumberUtil getNumberType:phoneNumber]; 83 | if (numberType == NBEPhoneNumberTypeUNKNOWN) { 84 | return nil; 85 | } else if (![_phoneNumberUtil isNumberGeographical:phoneNumber]) { 86 | return [self countryNameForNumber:phoneNumber withLanguageCode:languageCode]; 87 | } 88 | return [self descriptionForValidNumber:phoneNumber withLanguageCode:languageCode]; 89 | } 90 | 91 | - (nullable NSString *)descriptionForNumber:(NBPhoneNumber *)phoneNumber 92 | withLanguageCode:(NSString *)languageCode 93 | withUserRegion:(NSString *)userRegion { 94 | NBEPhoneNumberType numberType = [_phoneNumberUtil getNumberType:phoneNumber]; 95 | if (numberType == NBEPhoneNumberTypeUNKNOWN) { 96 | return nil; 97 | } else if (![_phoneNumberUtil isNumberGeographical:phoneNumber]) { 98 | return [self countryNameForNumber:phoneNumber withLanguageCode:languageCode]; 99 | } 100 | return [self descriptionForValidNumber:phoneNumber 101 | withLanguageCode:languageCode 102 | withUserRegion:userRegion]; 103 | } 104 | 105 | - (nullable NSString *)descriptionForNumber:(NBPhoneNumber *)phoneNumber { 106 | NBEPhoneNumberType numberType = [_phoneNumberUtil getNumberType:phoneNumber]; 107 | NSString *languageCode = [[NSLocale preferredLanguages] firstObject]; 108 | 109 | if (languageCode == nil) { 110 | return nil; 111 | } 112 | 113 | if (numberType == NBEPhoneNumberTypeUNKNOWN) { 114 | return nil; 115 | } else if (![_phoneNumberUtil isNumberGeographical:phoneNumber]) { 116 | return [self countryNameForNumber:phoneNumber withLanguageCode:languageCode]; 117 | } 118 | return [self descriptionForValidNumber:phoneNumber withLanguageCode:languageCode]; 119 | } 120 | 121 | - (nullable NSString *)descriptionForNumber:(NBPhoneNumber *)phoneNumber 122 | withUserRegion:(NSString *)userRegion { 123 | NBEPhoneNumberType numberType = [_phoneNumberUtil getNumberType:phoneNumber]; 124 | NSString *languageCode = [[NSLocale preferredLanguages] firstObject]; 125 | 126 | if (languageCode == nil) { 127 | return nil; 128 | } 129 | 130 | if (numberType == NBEPhoneNumberTypeUNKNOWN) { 131 | return nil; 132 | } else if (![_phoneNumberUtil isNumberGeographical:phoneNumber]) { 133 | return [self countryNameForNumber:phoneNumber withLanguageCode:languageCode]; 134 | } 135 | return [self descriptionForValidNumber:phoneNumber 136 | withLanguageCode:languageCode 137 | withUserRegion:userRegion]; 138 | } 139 | 140 | - (nullable NSString *)countryNameForNumber:(NBPhoneNumber *)number 141 | withLanguageCode:(NSString *)languageCode { 142 | NSArray *regionCodes = [_phoneNumberUtil getRegionCodesForCountryCode:number.countryCode]; 143 | if ([regionCodes count] == 1) { 144 | return [self regionDisplayName:regionCodes[0] withLanguageCode:languageCode]; 145 | } else { 146 | NSString *regionWhereNumberIsValid = INVALID_REGION_CODE; 147 | for (NSString *regionCode in regionCodes) { 148 | if ([_phoneNumberUtil isValidNumberForRegion:number regionCode:regionCode]) { 149 | if (![regionWhereNumberIsValid isEqualToString:INVALID_REGION_CODE]) { 150 | return nil; 151 | } 152 | regionWhereNumberIsValid = regionCode; 153 | } 154 | } 155 | 156 | return [self regionDisplayName:regionWhereNumberIsValid withLanguageCode:languageCode]; 157 | } 158 | } 159 | 160 | - (nullable NSString *)regionDisplayName:(NSString *)regionCode 161 | withLanguageCode:(NSString *)languageCode { 162 | if (regionCode == nil || [regionCode isEqualToString:INVALID_REGION_CODE] || 163 | [regionCode isEqual:NB_REGION_CODE_FOR_NON_GEO_ENTITY]) { 164 | return nil; 165 | } else { 166 | return [[NSLocale localeWithLocaleIdentifier:languageCode] displayNameForKey:NSLocaleCountryCode 167 | value:regionCode]; 168 | } 169 | } 170 | 171 | @end 172 | -------------------------------------------------------------------------------- /scripts/versionCommitter.swift: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env swift 2 | 3 | // 4 | // versionCommitter.swift 5 | // libPhoneNumber 6 | // 7 | // Created by Kris Kline on 12/3/25. 8 | // Copyright © 2025 Google. All rights reserved. 9 | // 10 | 11 | import Foundation 12 | 13 | // MARK: - Script Configuration 14 | 15 | let scriptVersion = "1.0.0" 16 | 17 | let scriptName = URL(fileURLWithPath: (CommandLine.arguments.first)!).lastPathComponent 18 | let scriptPath = URL(fileURLWithPath: (CommandLine.arguments.first)!).deletingLastPathComponent().path 19 | 20 | let dividingLine = "-----------------------------------------------------" 21 | let header = 22 | """ 23 | \(dividingLine) 24 | \(scriptName) v\(scriptVersion) 25 | """ 26 | let footer = 27 | """ 28 | \(dividingLine) 29 | """ 30 | 31 | // MARK: - String Extension 32 | 33 | extension String { 34 | /// Whether this string represents a numbered version: X.Y.Z, X.Y, or Z 35 | var isVersion: Bool { 36 | // Regex to match a common version format (e.g., X.Y.Z, X.Y, X) 37 | // where X, Y, Z are one or more digits. 38 | let versionPattern = #"^\d+(\.\d+){0,2}$"# 39 | 40 | do { 41 | let regex = try Regex(versionPattern) 42 | return self.wholeMatch(of: regex) != nil 43 | } catch { 44 | return false 45 | } 46 | } 47 | } 48 | 49 | // MARK: - Argument Parsing 50 | 51 | /// The ANSI code for resetting output text formatting 52 | let ANSI_RESET = "\u{001B}[0m" 53 | 54 | /// The ANSI code for making the text foreground color RED 55 | let ANSI_RED = "\u{001B}[31m" 56 | 57 | /** 58 | Prints out the usage information for this script to the console 59 | */ 60 | func printUsage() { 61 | print( 62 | """ 63 | Usage: \(scriptName) [-p|--push] [-r|--remote ] 64 | Example: \(scriptName) 1.2.3 -p -r original 65 | """ 66 | ) 67 | } 68 | 69 | /** 70 | Prints the specified error out to the console's standard error output 71 | 72 | - parameter errorString: The error string to print out 73 | */ 74 | func printError(_ errorString: String) { 75 | fputs("\(ANSI_RED)\(errorString)\n\(ANSI_RESET)", __stderrp) 76 | } 77 | 78 | /** 79 | Prints the specified error out to the console and exists the script 80 | 81 | - parameter exitCode: The exit code for ending the program 82 | - parameter errorString: The error string to print out 83 | */ 84 | func printErrorAndExit(_ exitCode: Int32, _ errorString: String) -> Never { 85 | printError(errorString) 86 | exit(exitCode) 87 | } 88 | 89 | var argVersion: String? 90 | var pushBranch = false 91 | var remoteName = "origin" 92 | 93 | var argIter: IndexingIterator.SubSequence> = CommandLine.arguments.dropFirst().makeIterator() 94 | 95 | while let arg: String = argIter.next() { 96 | switch arg { 97 | case "-p", "--push": 98 | pushBranch = true 99 | case "-r", "--remote": 100 | guard let tempName = argIter.next() else { 101 | printErrorAndExit(1, "ERROR: '--remote' or '-r' flag must be followed by the name of the remote to use") 102 | } 103 | remoteName = tempName 104 | default: 105 | if argVersion == nil, arg.isVersion { 106 | argVersion = arg 107 | } else { 108 | printErrorAndExit(1, "Unknown argument: \(arg)") 109 | } 110 | } 111 | } 112 | 113 | guard let newVersion = argVersion else { 114 | printUsage() 115 | printErrorAndExit(1, "ERROR: Version string is required.") 116 | } 117 | 118 | // MARK: - Helper Functions 119 | 120 | /** 121 | Runs the specified shell command and returns the output as a String, along with the exit code 122 | 123 | - parameter printOutput: Whether to print the output from the shell command (primarily used for debugging) 124 | - parameter args: The list of arguments to run in the shell 125 | 126 | - returns A tuple of the output as a string, and the exit code 127 | */ 128 | @discardableResult 129 | func shell(printOutput: Bool = false, _ args: String...) -> (output: String, exitCode: Int32) { 130 | let task = Process() 131 | let pipe = Pipe() 132 | 133 | task.standardOutput = pipe 134 | task.standardError = pipe 135 | task.arguments = ["-c", args.joined(separator: " ")] 136 | task.launchPath = "/bin/bash" 137 | task.launch() 138 | if printOutput { 139 | print("\n\n\(args.joined(separator: " "))") 140 | } 141 | print("", terminator: "") // helps with the 'git add' command failing sometimes for no reason 142 | let data = pipe.fileHandleForReading.readDataToEndOfFile() 143 | task.waitUntilExit() 144 | let output = String(data: data, encoding: .utf8) ?? "" 145 | if printOutput { 146 | print("==> Exit Code: \(task.terminationStatus)\n==>\n\(output)\n\n") 147 | } 148 | return (output, task.terminationStatus) 149 | } 150 | 151 | /** 152 | Exits the app after doing any necessary clean up 153 | 154 | - parameter exitCode: The exit code to use when exiting the application 155 | */ 156 | func exitApp(_ exitCode: Int32) -> Never { 157 | print(footer) 158 | exit(exitCode) 159 | } 160 | 161 | /** 162 | Exits the app after doing any necessary clean up 163 | 164 | - parameter exitCode: The exit code to use when exiting the application 165 | - parameter errorString: The error string to print when exiting 166 | */ 167 | func exitAppWithError(_ exitCode: Int32, _ errorString: String) -> Never { 168 | printError(errorString) 169 | exitApp(exitCode) 170 | } 171 | 172 | // MARK: - Script Execution 173 | 174 | print(header) 175 | 176 | print("New Version: \(newVersion)\n") 177 | 178 | let scriptToCall = "\(scriptPath)/updateProjectVersions.swift" 179 | 180 | print("Calling update script: \(scriptToCall) \(newVersion)") 181 | let (result, exitCode) = shell(scriptToCall, newVersion, "-n") 182 | 183 | if exitCode != 0 { 184 | exitAppWithError(2, "ERROR: Script \(scriptToCall) exited with status \(exitCode).") 185 | } 186 | 187 | let files = result 188 | .split(separator: "\n") 189 | .map { $0.trimmingCharacters(in: .whitespacesAndNewlines) } 190 | .filter { !$0.isEmpty } 191 | 192 | if files.isEmpty { 193 | print("No files to commit. Exiting.\n") 194 | exitApp(0) 195 | } 196 | 197 | print("Files to commit:") 198 | files.forEach { print(" \($0)") } 199 | 200 | // Create branch 201 | let branchName = "Version-\(newVersion)" 202 | print("\nCreating branch: \(branchName)") 203 | let (branchOutput, branchExit) = shell("git checkout -b \(branchName)") 204 | guard branchExit == 0 else { 205 | exitAppWithError(3, "ERROR: Failed to create branch \(branchName).\n==>\(branchExit)\n==>\(branchOutput)") 206 | } 207 | 208 | // Stage files 209 | print("\nAdding files to git staging area...") 210 | let addCommand = "git add \(files.map { "'\($0)'" }.joined(separator: " "))" 211 | let (addOutput, addExit) = shell(addCommand) 212 | guard addExit == 0 else { 213 | exitAppWithError(4, "ERROR: Failed to add files to git.\n==>\(addExit)\n==>\(addOutput)") 214 | } 215 | 216 | // Commit 217 | let commitMessage = "Update version to \(newVersion)" 218 | print("\nCommitting with message: \"\(commitMessage)\"") 219 | let (commitOutput, commitExit) = shell("git commit -m '\(commitMessage)'") 220 | guard commitExit == 0 else { 221 | exitAppWithError(5, "ERROR: Failed to commit files.\n==>\(commitExit)\n==>\(commitOutput)") 222 | } 223 | 224 | // Push 225 | if pushBranch { 226 | print("\nPushing branch to remote: \"\(remoteName)\"") 227 | let (pushOutput, pushExit) = shell("git push -u \(remoteName) \(branchName) -f") 228 | guard pushExit == 0 else { 229 | exitAppWithError(6, "ERROR: Failed to push branch.\n==>\(pushExit)\n==>\(pushOutput)") 230 | } 231 | } 232 | 233 | if pushBranch { 234 | print("\nSuccess! Branch \(branchName) created, committed, and pushed to remote: \(remoteName)") 235 | } else { 236 | print("\nSuccess! Branch \(branchName) created and committed.") 237 | } 238 | 239 | print(footer) 240 | --------------------------------------------------------------------------------