From 27c058a9b80b4be132505dff1421a6edbaf1b6e2 Mon Sep 17 00:00:00 2001 From: Dhaya <154633+dhayab@users.noreply.github.com> Date: Mon, 4 Aug 2025 09:51:04 +0200 Subject: [PATCH] feat(ios): add getting-started example --- instantsearch-ios/getting-started/README.md | 16 + .../SearchApp.xcodeproj/project.pbxproj | 353 ++++++++++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/swiftpm/Package.resolved | 51 +++ .../UserInterfaceState.xcuserstate | Bin 0 -> 30547 bytes .../xcschemes/xcschememanagement.plist | 14 + .../AccentColor.colorset/Contents.json | 11 + .../AppIcon.appiconset/Contents.json | 35 ++ .../SearchApp/Assets.xcassets/Contents.json | 6 + .../SearchApp/ContentView.swift | 139 +++++++ .../SearchApp/SearchAppApp.swift | 26 ++ 11 files changed, 658 insertions(+) create mode 100644 instantsearch-ios/getting-started/README.md create mode 100644 instantsearch-ios/getting-started/SearchApp.xcodeproj/project.pbxproj create mode 100644 instantsearch-ios/getting-started/SearchApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 instantsearch-ios/getting-started/SearchApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved create mode 100644 instantsearch-ios/getting-started/SearchApp.xcodeproj/project.xcworkspace/xcuserdata/dhaya.benmessaoud.xcuserdatad/UserInterfaceState.xcuserstate create mode 100644 instantsearch-ios/getting-started/SearchApp.xcodeproj/xcuserdata/dhaya.benmessaoud.xcuserdatad/xcschemes/xcschememanagement.plist create mode 100644 instantsearch-ios/getting-started/SearchApp/Assets.xcassets/AccentColor.colorset/Contents.json create mode 100644 instantsearch-ios/getting-started/SearchApp/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 instantsearch-ios/getting-started/SearchApp/Assets.xcassets/Contents.json create mode 100644 instantsearch-ios/getting-started/SearchApp/ContentView.swift create mode 100644 instantsearch-ios/getting-started/SearchApp/SearchAppApp.swift diff --git a/instantsearch-ios/getting-started/README.md b/instantsearch-ios/getting-started/README.md new file mode 100644 index 000000000..d1c88cc5b --- /dev/null +++ b/instantsearch-ios/getting-started/README.md @@ -0,0 +1,16 @@ +# InstantSearch iOS getting started sample + +This sample shows how to get started on building your search experience by easily adding common InstantSearch components. + +## Get started + +Clone the getting started sample: + +```sh +curl https://codeload.github.com/algolia/doc-code-samples/tar.gz/master | tar -xz --strip=2 doc-code-samples-master/instantsearch-ios/getting-started +``` + +Open `SearchApp.xcodeproj` in Xcode, and run the app on a simulator or device. + +## Additional resources +Learn more about InstantSearch iOS [widgets](https://www.algolia.com/doc/guides/building-search-ui/widgets/showcase/ios/) in the Algolia documentation. diff --git a/instantsearch-ios/getting-started/SearchApp.xcodeproj/project.pbxproj b/instantsearch-ios/getting-started/SearchApp.xcodeproj/project.pbxproj new file mode 100644 index 000000000..d2491342c --- /dev/null +++ b/instantsearch-ios/getting-started/SearchApp.xcodeproj/project.pbxproj @@ -0,0 +1,353 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 77; + objects = { + +/* Begin PBXBuildFile section */ + 25A9E5EB2E3CBC61003F2C5E /* InstantSearchSwiftUI in Frameworks */ = {isa = PBXBuildFile; productRef = 25A9E5EA2E3CBC61003F2C5E /* InstantSearchSwiftUI */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 25A9E5DB2E3CBBFB003F2C5E /* SearchApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SearchApp.app; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFileSystemSynchronizedRootGroup section */ + 25A9E5DD2E3CBBFB003F2C5E /* SearchApp */ = { + isa = PBXFileSystemSynchronizedRootGroup; + path = SearchApp; + sourceTree = ""; + }; +/* End PBXFileSystemSynchronizedRootGroup section */ + +/* Begin PBXFrameworksBuildPhase section */ + 25A9E5D82E3CBBFB003F2C5E /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 25A9E5EB2E3CBC61003F2C5E /* InstantSearchSwiftUI in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 25A9E5D22E3CBBFB003F2C5E = { + isa = PBXGroup; + children = ( + 25A9E5DD2E3CBBFB003F2C5E /* SearchApp */, + 25A9E5DC2E3CBBFB003F2C5E /* Products */, + ); + sourceTree = ""; + }; + 25A9E5DC2E3CBBFB003F2C5E /* Products */ = { + isa = PBXGroup; + children = ( + 25A9E5DB2E3CBBFB003F2C5E /* SearchApp.app */, + ); + name = Products; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 25A9E5DA2E3CBBFB003F2C5E /* SearchApp */ = { + isa = PBXNativeTarget; + buildConfigurationList = 25A9E5E62E3CBBFC003F2C5E /* Build configuration list for PBXNativeTarget "SearchApp" */; + buildPhases = ( + 25A9E5D72E3CBBFB003F2C5E /* Sources */, + 25A9E5D82E3CBBFB003F2C5E /* Frameworks */, + 25A9E5D92E3CBBFB003F2C5E /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + fileSystemSynchronizedGroups = ( + 25A9E5DD2E3CBBFB003F2C5E /* SearchApp */, + ); + name = SearchApp; + packageProductDependencies = ( + 25A9E5EA2E3CBC61003F2C5E /* InstantSearchSwiftUI */, + ); + productName = SearchApp; + productReference = 25A9E5DB2E3CBBFB003F2C5E /* SearchApp.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 25A9E5D32E3CBBFB003F2C5E /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1640; + LastUpgradeCheck = 1640; + TargetAttributes = { + 25A9E5DA2E3CBBFB003F2C5E = { + CreatedOnToolsVersion = 16.4; + }; + }; + }; + buildConfigurationList = 25A9E5D62E3CBBFB003F2C5E /* Build configuration list for PBXProject "SearchApp" */; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 25A9E5D22E3CBBFB003F2C5E; + minimizedProjectReferenceProxies = 1; + packageReferences = ( + 25A9E5E92E3CBC61003F2C5E /* XCRemoteSwiftPackageReference "instantsearch-ios" */, + ); + preferredProjectObjectVersion = 77; + productRefGroup = 25A9E5DC2E3CBBFB003F2C5E /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 25A9E5DA2E3CBBFB003F2C5E /* SearchApp */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 25A9E5D92E3CBBFB003F2C5E /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 25A9E5D72E3CBBFB003F2C5E /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 25A9E5E42E3CBBFC003F2C5E /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 18.5; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 25A9E5E52E3CBBFC003F2C5E /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 18.5; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 25A9E5E72E3CBBFC003F2C5E /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.SearchApp; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 25A9E5E82E3CBBFC003F2C5E /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.SearchApp; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 25A9E5D62E3CBBFB003F2C5E /* Build configuration list for PBXProject "SearchApp" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 25A9E5E42E3CBBFC003F2C5E /* Debug */, + 25A9E5E52E3CBBFC003F2C5E /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 25A9E5E62E3CBBFC003F2C5E /* Build configuration list for PBXNativeTarget "SearchApp" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 25A9E5E72E3CBBFC003F2C5E /* Debug */, + 25A9E5E82E3CBBFC003F2C5E /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + +/* Begin XCRemoteSwiftPackageReference section */ + 25A9E5E92E3CBC61003F2C5E /* XCRemoteSwiftPackageReference "instantsearch-ios" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/algolia/instantsearch-ios"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 7.27.0; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 25A9E5EA2E3CBC61003F2C5E /* InstantSearchSwiftUI */ = { + isa = XCSwiftPackageProductDependency; + package = 25A9E5E92E3CBC61003F2C5E /* XCRemoteSwiftPackageReference "instantsearch-ios" */; + productName = InstantSearchSwiftUI; + }; +/* End XCSwiftPackageProductDependency section */ + }; + rootObject = 25A9E5D32E3CBBFB003F2C5E /* Project object */; +} diff --git a/instantsearch-ios/getting-started/SearchApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/instantsearch-ios/getting-started/SearchApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..919434a62 --- /dev/null +++ b/instantsearch-ios/getting-started/SearchApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/instantsearch-ios/getting-started/SearchApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/instantsearch-ios/getting-started/SearchApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 000000000..8158fc8ba --- /dev/null +++ b/instantsearch-ios/getting-started/SearchApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,51 @@ +{ + "originHash" : "ea8f27e1a2bd7273484d2c19c8b43a41b1dcca14ac8ee25619ca2c95f332ef90", + "pins" : [ + { + "identity" : "algoliasearch-client-swift", + "kind" : "remoteSourceControl", + "location" : "https://github.com/algolia/algoliasearch-client-swift", + "state" : { + "revision" : "4322ab2dc8cdb7acb6d06a019fd56f84dc998f25", + "version" : "8.21.0" + } + }, + { + "identity" : "instantsearch-ios", + "kind" : "remoteSourceControl", + "location" : "https://github.com/algolia/instantsearch-ios", + "state" : { + "revision" : "5f94daaff8e0e75f4793ecd14cc45efae5f50a24", + "version" : "7.27.0" + } + }, + { + "identity" : "instantsearch-telemetry-native", + "kind" : "remoteSourceControl", + "location" : "https://github.com/algolia/instantsearch-telemetry-native", + "state" : { + "revision" : "d9861ceb2fe25899a31672fb7c648ae8036d3221", + "version" : "0.1.3" + } + }, + { + "identity" : "swift-log", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-log", + "state" : { + "revision" : "ce592ae52f982c847a4efc0dd881cc9eb32d29f2", + "version" : "1.6.4" + } + }, + { + "identity" : "swift-protobuf", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-protobuf.git", + "state" : { + "revision" : "102a647b573f60f73afdce5613a51d71349fe507", + "version" : "1.30.0" + } + } + ], + "version" : 3 +} diff --git a/instantsearch-ios/getting-started/SearchApp.xcodeproj/project.xcworkspace/xcuserdata/dhaya.benmessaoud.xcuserdatad/UserInterfaceState.xcuserstate b/instantsearch-ios/getting-started/SearchApp.xcodeproj/project.xcworkspace/xcuserdata/dhaya.benmessaoud.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000000000000000000000000000000000000..80d268beddf42100fab461153addd4c5ebe81a4d GIT binary patch literal 30547 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 KVf)qg?f(OYA@yzm literal 0 HcmV?d00001 diff --git a/instantsearch-ios/getting-started/SearchApp.xcodeproj/xcuserdata/dhaya.benmessaoud.xcuserdatad/xcschemes/xcschememanagement.plist b/instantsearch-ios/getting-started/SearchApp.xcodeproj/xcuserdata/dhaya.benmessaoud.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 000000000..af1bfce90 --- /dev/null +++ b/instantsearch-ios/getting-started/SearchApp.xcodeproj/xcuserdata/dhaya.benmessaoud.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,14 @@ + + + + + SchemeUserState + + SearchApp.xcscheme_^#shared#^_ + + orderHint + 0 + + + + diff --git a/instantsearch-ios/getting-started/SearchApp/Assets.xcassets/AccentColor.colorset/Contents.json b/instantsearch-ios/getting-started/SearchApp/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 000000000..eb8789700 --- /dev/null +++ b/instantsearch-ios/getting-started/SearchApp/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/instantsearch-ios/getting-started/SearchApp/Assets.xcassets/AppIcon.appiconset/Contents.json b/instantsearch-ios/getting-started/SearchApp/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 000000000..230588010 --- /dev/null +++ b/instantsearch-ios/getting-started/SearchApp/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,35 @@ +{ + "images" : [ + { + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/instantsearch-ios/getting-started/SearchApp/Assets.xcassets/Contents.json b/instantsearch-ios/getting-started/SearchApp/Assets.xcassets/Contents.json new file mode 100644 index 000000000..73c00596a --- /dev/null +++ b/instantsearch-ios/getting-started/SearchApp/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/instantsearch-ios/getting-started/SearchApp/ContentView.swift b/instantsearch-ios/getting-started/SearchApp/ContentView.swift new file mode 100644 index 000000000..e424adfb3 --- /dev/null +++ b/instantsearch-ios/getting-started/SearchApp/ContentView.swift @@ -0,0 +1,139 @@ +// +// ContentView.swift +// SearchApp +// +// Created by Algolia on 01/08/2025. +// + +import InstantSearchCore +import InstantSearchSwiftUI +import SwiftUI + +struct Product: Codable { + let name: String +} + +class AlgoliaController { + let searcher: HitsSearcher + + let searchBoxInteractor: SearchBoxInteractor + let searchBoxController: SearchBoxObservableController + + let hitsInteractor: HitsInteractor + let hitsController: HitsObservableController + + let statsInteractor: StatsInteractor + let statsController: StatsTextObservableController + + let filterState: FilterState + + let facetListInteractor: FacetListInteractor + let facetListController: FacetListObservableController + + init() { + self.searcher = HitsSearcher(appID: "latency", + apiKey: "1f6fd3a6fb973cb08419fe7d288fa4db", + indexName: "bestbuy") + self.searchBoxInteractor = .init() + self.searchBoxController = .init() + self.hitsInteractor = .init() + self.hitsController = .init() + self.statsInteractor = .init() + self.statsController = .init() + self.filterState = .init() + self.facetListInteractor = .init() + self.facetListController = .init() + + setupConnections() + } + + func setupConnections() { + searchBoxInteractor.connectSearcher(searcher) + searchBoxInteractor.connectController(searchBoxController) + hitsInteractor.connectSearcher(searcher) + hitsInteractor.connectController(hitsController) + statsInteractor.connectSearcher(searcher) + statsInteractor.connectController(statsController) + searcher.connectFilterState(filterState) + facetListInteractor.connectSearcher(searcher, with: "manufacturer") + facetListInteractor.connectFilterState(filterState, with: "manufacturer", operator: .or) + facetListInteractor.connectController(facetListController, with: FacetListPresenter(sortBy: [.isRefined, .count(order: .descending)])) + } +} + +struct ContentView: View { + @ObservedObject var searchBoxController: SearchBoxObservableController + @ObservedObject var hitsController: HitsObservableController + @ObservedObject var statsController: StatsTextObservableController + @ObservedObject var facetListController: FacetListObservableController + + @State private var isEditing = false + @State private var isPresentingFacets = false + + var body: some View { + VStack(spacing: 7) { + Text(statsController.stats) + .fontWeight(.medium) + HitsList(hitsController) { hit, _ in + VStack(alignment: .leading, spacing: 10) { + Text(hit?.name ?? "") + .padding(.all, 10) + Divider() + } + } noResults: { + Text("No Results") + .frame(maxWidth: .infinity, maxHeight: .infinity) + } + } + .padding(.horizontal) + .padding(.top, 10) + .searchable(text: $searchBoxController.query) + .navigationBarTitle("Algolia & SwiftUI") + .navigationBarItems(trailing: facetsButton()) + .sheet(isPresented: $isPresentingFacets, content: facets) + } + + @ViewBuilder + private func facets() -> some View { + NavigationView { + ScrollView { + FacetList(facetListController) { facet, isSelected in + VStack { + FacetRow(facet: facet, isSelected: isSelected) + .padding() + Divider() + } + } noResults: { + Text("No facet found") + .frame(maxWidth: .infinity, maxHeight: .infinity) + } + } + .navigationBarTitle("Brand") + } + } + + private func facetsButton() -> some View { + Button(action: { + isPresentingFacets.toggle() + }, label: { + Image(systemName: "line.horizontal.3.decrease.circle") + .font(.title) + }) + } + + +} + +struct ContentView_Previews: PreviewProvider { + static let algoliaController = AlgoliaController() + static var previews: some View { + NavigationView { + ContentView(searchBoxController: algoliaController.searchBoxController, + hitsController: algoliaController.hitsController, + statsController: algoliaController.statsController, + facetListController: algoliaController.facetListController) + }.onAppear { + algoliaController.searcher.search() + } + } +} diff --git a/instantsearch-ios/getting-started/SearchApp/SearchAppApp.swift b/instantsearch-ios/getting-started/SearchApp/SearchAppApp.swift new file mode 100644 index 000000000..c8b2c2e0e --- /dev/null +++ b/instantsearch-ios/getting-started/SearchApp/SearchAppApp.swift @@ -0,0 +1,26 @@ +// +// SearchAppApp.swift +// SearchApp +// +// Created by Algolia on 01/08/2025. +// + +import SwiftUI + +@main +struct SearchAppApp: App { + private var algoliaController = AlgoliaController() + var body: some Scene { + WindowGroup { + NavigationStack { + ContentView(searchBoxController: algoliaController.searchBoxController, + hitsController: algoliaController.hitsController, + statsController: algoliaController.statsController, + facetListController: algoliaController.facetListController) + } + .onAppear { + algoliaController.searcher.search() + } + } + } +}