Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -77,4 +77,7 @@ TestCase
/test-reports
/TestCase
/xcov_output
/html
/html

# Contentstack regions registry — downloaded by Scripts/download-regions.sh, never committed
Sources/ContentstackUtils/Resources/regions.json
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ All notable changes to this project will be documented in this file.

- 2026-04-02: Added CocoaPods deprecation guidance for **new** projects—**SPM** is recommended for ContentstackUtils; clarified companion role vs the core Swift CDA SDK. Updated README (Important section), added root **DEPRECATION.md** (customer-facing only), added **Docs/overview.md** banner with link to `DEPRECATION.md`.

## [1.6.0] - 2026-06-26

### Added

- **Region endpoint resolution** — `Endpoint.getContentstackEndpoint` resolves service endpoints dynamically from the Contentstack Regions Registry (alias-aware, case-insensitive), with `Endpoint.EndpointError` on invalid input.

## [1.5.0] - 2026-03-31

- **`getVariantMetadataTags`** is the canonical API for `data-csvariants`; **`getDataCsvariantsAttribute`** is deprecated (delegates to it until removed in a major release).
Expand Down
30 changes: 30 additions & 0 deletions ContentstackUtils.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@
6749AC902F714E26007282C5 /* variantsEntries.json in Resources */ = {isa = PBXBuildFile; fileRef = 6749AC8F2F714E26007282C5 /* variantsEntries.json */; };
6749AC922F714E2F007282C5 /* variantsSingleEntry.json in Resources */ = {isa = PBXBuildFile; fileRef = 6749AC912F714E2F007282C5 /* variantsSingleEntry.json */; };
6749AC942F714E36007282C5 /* VariantUtilityTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6749AC932F714E36007282C5 /* VariantUtilityTests.swift */; };
679382562FD96042007C4158 /* Endpoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 679382552FD96042007C4158 /* Endpoint.swift */; };
679382572FD96042007C4158 /* Endpoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 679382552FD96042007C4158 /* Endpoint.swift */; };
679382582FD96042007C4158 /* Endpoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 679382552FD96042007C4158 /* Endpoint.swift */; };
6793825B2FD9606B007C4158 /* EndpointTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6793825A2FD9606B007C4158 /* EndpointTests.swift */; };
OBJ_22 /* ContentstackUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_9 /* ContentstackUtils.swift */; };
OBJ_29 /* Package.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_6 /* Package.swift */; };
OBJ_40 /* ContentstackUtilsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_12 /* ContentstackUtilsTests.swift */; };
Expand Down Expand Up @@ -141,10 +145,15 @@
0FFF2F292668FC54003E9DBF /* NodeType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NodeType.swift; sourceTree = "<group>"; };
0FFF2F372668FE85003E9DBF /* Node.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Node.swift; sourceTree = "<group>"; };
64F522122BF5F3F300AE6E0F /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
670DAEA12FD9637200FB27D9 /* TestPlan.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; name = TestPlan.xctestplan; path = Tests/ContentstackUtilsTests/TestPlan.xctestplan; sourceTree = "<group>"; };
6749AC8F2F714E26007282C5 /* variantsEntries.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = variantsEntries.json; sourceTree = "<group>"; };
6749AC912F714E2F007282C5 /* variantsSingleEntry.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = variantsSingleEntry.json; sourceTree = "<group>"; };
6749AC932F714E36007282C5 /* VariantUtilityTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VariantUtilityTests.swift; sourceTree = "<group>"; };
6749AC952F715507007282C5 /* CHANGELOG.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = CHANGELOG.md; sourceTree = "<group>"; };
679382552FD96042007C4158 /* Endpoint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Endpoint.swift; sourceTree = "<group>"; };
679382592FD9604A007C4158 /* ContentstackUtilsPackageTests-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "ContentstackUtilsPackageTests-Bridging-Header.h"; sourceTree = "<group>"; };
6793825A2FD9606B007C4158 /* EndpointTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EndpointTests.swift; sourceTree = "<group>"; };
6793825C2FD96097007C4158 /* download-regions.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = "download-regions.sh"; sourceTree = "<group>"; };
"ContentstackUtils::ContentstackUtils::Product" /* ContentstackUtils.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = ContentstackUtils.framework; sourceTree = BUILT_PRODUCTS_DIR; };
"ContentstackUtils::ContentstackUtilsTests::Product" /* ContentstackUtilsTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; path = ContentstackUtilsTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
OBJ_12 /* ContentstackUtilsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentstackUtilsTests.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -193,11 +202,13 @@
children = (
0FEC37AF254FFF6E00B1EFDD /* Metadata.swift */,
0F00785C26A6ACBF00FC4925 /* GQLEmbededEntry.swift */,
679382552FD96042007C4158 /* Endpoint.swift */,
0F00785E26A6ACDC00FC4925 /* GQLEmbededAsset.swift */,
0F00786026A6AD0100FC4925 /* JSONNode.swift */,
0F00786226A6AD2100FC4925 /* JSONNodes.swift */,
0F00786426A6AD3E00FC4925 /* Edges.swift */,
0F00786626A6AD6800FC4925 /* ConnectionNode.swift */,
679382592FD9604A007C4158 /* ContentstackUtilsPackageTests-Bridging-Header.h */,
);
name = Models;
sourceTree = "<group>";
Expand Down Expand Up @@ -243,6 +254,7 @@
isa = PBXGroup;
children = (
0FA3D58F252228E300E58179 /* build.sh */,
6793825C2FD96097007C4158 /* download-regions.sh */,
0FA3D5902522290700E58179 /* run-test-case.sh */,
);
path = Scripts;
Expand Down Expand Up @@ -333,6 +345,7 @@
6749AC932F714E36007282C5 /* VariantUtilityTests.swift */,
0F7142C325514A6F00C18A61 /* ContentstackUtilsArrayTest.swift */,
0F7142C52551684600C18A61 /* ContentstackUtilsCustomRendertest.swift */,
6793825A2FD9606B007C4158 /* EndpointTests.swift */,
0F579540266A50D40082815C /* MarkTypeTest.swift */,
0F579546266A50E30082815C /* NodeTypeTest.swift */,
0FFD88D6266DDD1900BA5919 /* ContentstackUtilsJsonToHtmlTest.swift */,
Expand All @@ -354,6 +367,7 @@
OBJ_5 = {
isa = PBXGroup;
children = (
670DAEA12FD9637200FB27D9 /* TestPlan.xctestplan */,
64F522122BF5F3F300AE6E0F /* PrivacyInfo.xcprivacy */,
0FAA3EBD26A1C65B00173FA9 /* ContentstackUtils.podspec */,
OBJ_6 /* Package.swift */,
Expand Down Expand Up @@ -454,6 +468,11 @@
BuildIndependentTargetsInParallel = YES;
LastSwiftMigration = 9999;
LastUpgradeCheck = 1620;
TargetAttributes = {
"ContentstackUtils::ContentstackUtilsPackageTests::ProductTarget" = {
LastSwiftMigration = 2620;
};
};
};
buildConfigurationList = OBJ_2 /* Build configuration list for PBXProject "ContentstackUtils" */;
compatibilityVersion = "Xcode 3.2";
Expand Down Expand Up @@ -540,13 +559,15 @@
0F00786326A6AD2100FC4925 /* JSONNodes.swift in Sources */,
0F00785D26A6ACBF00FC4925 /* GQLEmbededEntry.swift in Sources */,
OBJ_22 /* ContentstackUtils.swift in Sources */,
679382562FD96042007C4158 /* Endpoint.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
OBJ_28 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 0;
files = (
679382582FD96042007C4158 /* Endpoint.swift in Sources */,
OBJ_29 /* Package.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand All @@ -565,13 +586,15 @@
0FEC37BA25503E5000B1EFDD /* CustomRenderOptionMock.swift in Sources */,
0FA3D58A252207B000E58179 /* DefaultRenderTests.swift in Sources */,
0FEC0B3B254FEC60008D4E66 /* MetadataTests.swift in Sources */,
6793825B2FD9606B007C4158 /* EndpointTests.swift in Sources */,
0FA3D58D2522098000E58179 /* EmbededModelMock.swift in Sources */,
0FFD88D7266DDD1900BA5919 /* ContentstackUtilsJsonToHtmlTest.swift in Sources */,
0F07E62F25244DB5003E0BD1 /* StringExtensionTests.swift in Sources */,
0F7142C425514A6F00C18A61 /* ContentstackUtilsArrayTest.swift in Sources */,
6749AC942F714E36007282C5 /* VariantUtilityTests.swift in Sources */,
0FFD88EE266DE1A600BA5919 /* NodeParser.swift in Sources */,
0FFD88F7266DE1FB00BA5919 /* JsonNodes.swift in Sources */,
679382572FD96042007C4158 /* Endpoint.swift in Sources */,
0F00785B26A5A0EB00FC4925 /* GQLJsonToHtml.swift in Sources */,
0F00785926A59D6600FC4925 /* GQLJsonRTE.swift in Sources */,
OBJ_41 /* XCTestManifests.swift in Sources */,
Expand Down Expand Up @@ -756,14 +779,21 @@
OBJ_32 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_MODULES = YES;
DEAD_CODE_STRIPPING = YES;
SWIFT_OBJC_BRIDGING_HEADER = "Sources/ContentstackUtils/ContentstackUtilsPackageTests-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 6.0;
};
name = Debug;
};
OBJ_33 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_MODULES = YES;
DEAD_CODE_STRIPPING = YES;
SWIFT_OBJC_BRIDGING_HEADER = "Sources/ContentstackUtils/ContentstackUtilsPackageTests-Bridging-Header.h";
SWIFT_VERSION = 6.0;
};
name = Release;
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1620"
version = "1.3">
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
Expand All @@ -28,6 +28,20 @@
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "ContentstackUtils::ContentstackUtilsTests"
BuildableName = "ContentstackUtilsTests.xctest"
BlueprintName = "ContentstackUtilsTests"
ReferencedContainer = "container:ContentstackUtils.xcodeproj">
</BuildableReference>
<LocationScenarioReference
identifier = "com.apple.dt.IDEFoundation.CurrentLocationScenarioIdentifier"
referenceType = "1">
</LocationScenarioReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
Expand Down
19 changes: 15 additions & 4 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// swift-tools-version:5.1
// swift-tools-version:5.3
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription
Expand Down Expand Up @@ -53,11 +53,22 @@ let package = Package(
.target(
name: "ContentstackUtils",
dependencies: dependencies,
path: "Sources"),
path: "Sources",
resources: [
// regions.json is downloaded by Scripts/download-regions.sh and is NOT committed.
// SPM bundles it when present; Endpoint falls back to HTTP download when absent.
.process("ContentstackUtils/Resources")
]
),
.testTarget(
name: "ContentstackUtilsTests",
dependencies: ["ContentstackUtils"]),

dependencies: ["ContentstackUtils"],
resources: [
.process("EntryEmbedded.json"),
.process("variantsEntries.json"),
.process("variantsSingleEntry.json")
]),

],
swiftLanguageVersions: [.v5]
)
51 changes: 51 additions & 0 deletions Scripts/download-regions.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#!/usr/bin/env bash
#
# Downloads the Contentstack regions registry from the official source and
# saves it to Sources/ContentstackUtils/Resources/regions.json.
#
# Run before building so SPM bundles the file into the module:
# bash Scripts/download-regions.sh && swift build
#
# Also invoked manually to refresh the cached data:
# bash Scripts/download-regions.sh
#
# Requires: curl (preferred) or wget as fallback

set -euo pipefail

URL="https://artifacts.contentstack.com/regions.json"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
DEST="${SCRIPT_DIR}/../Sources/ContentstackUtils/Resources/regions.json"
DIR="$(dirname "$DEST")"

mkdir -p "$DIR"

data=""

# --- Attempt 1: curl (preferred) --------------------------------------------
if command -v curl &>/dev/null; then
data=$(curl --silent --fail --location --max-time 30 "$URL") || data=""
fi

# --- Attempt 2: wget fallback -----------------------------------------------
if [[ -z "$data" ]] && command -v wget &>/dev/null; then
data=$(wget --quiet --timeout=30 -O - "$URL") || data=""
fi

# --- Validate and write ------------------------------------------------------
if [[ -z "$data" ]]; then
echo "contentstack/utils: Warning — could not download regions.json." >&2
echo " The SDK will attempt to download it at runtime on first use." >&2
exit 0 # non-fatal: runtime fallback in Endpoint.swift handles it
fi

# Basic validation: must contain a "regions" key
if ! echo "$data" | grep -q '"regions"'; then
echo "contentstack/utils: Warning — downloaded data is not valid regions.json." >&2
exit 0
fi

echo "$data" > "$DEST"

region_count=$(echo "$data" | grep -o '"id"' | wc -l | tr -d ' ')
echo "contentstack/utils: regions.json downloaded (${region_count} regions)."
12 changes: 12 additions & 0 deletions Sources/ContentstackUtils/ContentstackUtils.swift
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,18 @@ public struct ContentstackUtils {
}
}

/// Proxy for `Endpoint.getContentstackEndpoint(_:_:_:)`.
/// Both calls produce identical results; this exists so callers already using
/// `ContentstackUtils.` don't need to change their import.
@discardableResult
public static func getContentstackEndpoint(
_ region: String = "us",
_ service: String = "",
_ omitHttps: Bool = false
) throws -> Any {
return try Endpoint.getContentstackEndpoint(region, service, omitHttps)
}

private static func jsonString(for array: [[String: Any]]) throws -> String{
let data = try JSONSerialization.data(withJSONObject: array, options: [])
guard let json = String(data: data, encoding: .utf8) else {
Expand Down
Loading
Loading