Skip to content
Open
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
4 changes: 2 additions & 2 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
[submodule "ios/Classes/countly-sdk-ios"]
path = ios/Classes/countly-sdk-ios
[submodule "ios/countly_flutter/Sources/countly_flutter/countly-sdk-ios"]
path = ios/countly_flutter/Sources/countly_flutter/countly-sdk-ios
url = https://github.com/Countly/countly-sdk-ios.git
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
## XX.XX.XX
* ! Minor breaking change ! The iOS plugin sources were reorganized to support Swift Package Manager. If you use a Notification Service Extension for rich push notifications, update the reference to "CountlyNotificationService.h" and "CountlyNotificationService.m" in your extension target to the new path "ios/countly_flutter/Sources/countly_flutter/countly-sdk-ios/".

* Added Swift Package Manager (SwiftPM) support for iOS. CocoaPods integration is still supported.

* Mitigated an issue where async native callbacks could crash with a NullPointerException when invoked after the Flutter engine had detached (e.g. hot restart, multi-engine setups) in Android.

## 26.1.0
Expand Down
4 changes: 2 additions & 2 deletions example/ios/Runner.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@
/* Begin PBXFileReference section */
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
2F5416FE25A893D50047E5F9 /* CountlyNotificationService.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = CountlyNotificationService.m; path = "../../ios/Classes/countly-sdk-ios/CountlyNotificationService.m"; sourceTree = "<group>"; };
2F54170025A893D70047E5F9 /* CountlyNotificationService.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = CountlyNotificationService.h; path = ".symlinks/plugins/countly_flutter/ios/Classes/countly-sdk-ios/../../../../../../../../ios/Classes/countly-sdk-ios/CountlyNotificationService.h"; sourceTree = "<group>"; };
2F5416FE25A893D50047E5F9 /* CountlyNotificationService.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = CountlyNotificationService.m; path = "../../ios/countly_flutter/Sources/countly_flutter/countly-sdk-ios/CountlyNotificationService.m"; sourceTree = "<group>"; };
2F54170025A893D70047E5F9 /* CountlyNotificationService.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = CountlyNotificationService.h; path = "../../ios/countly_flutter/Sources/countly_flutter/countly-sdk-ios/CountlyNotificationService.h"; sourceTree = "<group>"; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
43A5439BF4A8475C0F1E7A45 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
Expand Down
6 changes: 5 additions & 1 deletion ios/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,8 @@ Icon?
.tags*

/Flutter/Generated.xcconfig
/Flutter/flutter_export_environment.sh
/Flutter/flutter_export_environment.sh

# Swift Package Manager
.build/
.swiftpm/
6 changes: 4 additions & 2 deletions ios/countly_flutter.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@ Pod::Spec.new do |s|
s.social_media_url = 'https://twitter.com/gocountly'
s.author = {'Countly' => 'hello@count.ly'}
s.source = { :path => '.' }
s.source_files = 'Classes/**/*'
s.public_header_files = 'Classes/CountlyFlutterPlugin.h'
# Sources live in the Swift Package Manager layout; CocoaPods and SwiftPM share the same files.
s.source_files = 'countly_flutter/Sources/countly_flutter/**/*.{h,m,swift}'
s.public_header_files = 'countly_flutter/Sources/countly_flutter/include/countly_flutter/CountlyFlutterPlugin.h'
s.resource_bundles = { 'countly_flutter_privacy' => ['countly_flutter/Sources/countly_flutter/countly-sdk-ios/PrivacyInfo.xcprivacy'] }
s.dependency 'Flutter'
s.swift_version = '5.0'
s.ios.deployment_target = '10.0'
Expand Down
58 changes: 58 additions & 0 deletions ios/countly_flutter/Package.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// swift-tools-version: 5.9
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription

let package = Package(
name: "countly_flutter",
platforms: [
// Matches the vendored Countly iOS SDK (its Package.swift and podspecs all target iOS 10),
// and this plugin's own CocoaPods podspec (10.0). A package floor at or below the consumer's
// deployment target is required — SwiftPM rejects a package minimum higher than the app target.
.iOS(.v10)
],
products: [
// The library name replaces "_" with "-" per SwiftPM convention.
.library(name: "countly-flutter", targets: ["countly_flutter"])
],
dependencies: [],
targets: [
.target(
name: "countly_flutter",
dependencies: [],
// The Countly iOS SDK is vendored as a git submodule under countly-sdk-ios/.
// Exclude its non-source files, plus the unused default Swift plugin stub
// (SwiftPM does not allow Swift and Objective-C in the same target).
exclude: [
"SwiftCountlyFlutterPlugin.swift",
"countly-sdk-ios/CHANGELOG.md",
"countly-sdk-ios/README.md",
"countly-sdk-ios/SECURITY.md",
"countly-sdk-ios/LICENSE",
"countly-sdk-ios/Countly.podspec",
"countly-sdk-ios/Countly-PL.podspec",
"countly-sdk-ios/countly_dsym_uploader.sh",
"countly-sdk-ios/format.sh"
],
resources: [
.process("countly-sdk-ios/PrivacyInfo.xcprivacy")
],
cSettings: [
// Resolve the flat #import "..." statements used by the bridge and the
// vendored Countly iOS SDK without editing every source file.
.headerSearchPath("include/countly_flutter"),
.headerSearchPath("countly-sdk-ios"),
.headerSearchPath(".")
],
linkerSettings: [
.linkedFramework("Foundation"),
.linkedFramework("UIKit"),
.linkedFramework("UserNotifications"),
.linkedFramework("CoreLocation"),
.linkedFramework("WebKit"),
.linkedFramework("CoreTelephony"),
.linkedFramework("WatchConnectivity")
]
)
]
)
6 changes: 4 additions & 2 deletions scripts/no-push-files/countly_flutter_np.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@ Pod::Spec.new do |s|
s.social_media_url = 'https://twitter.com/gocountly'
s.author = {'Countly' => 'hello@count.ly'}
s.source = { :path => '.' }
s.source_files = 'Classes/**/*'
s.public_header_files = 'Classes/CountlyFlutterPlugin.h'
# Sources live in the Swift Package Manager layout; CocoaPods and SwiftPM share the same files.
s.source_files = 'countly_flutter_np/Sources/countly_flutter_np/**/*.{h,m,swift}'
s.public_header_files = 'countly_flutter_np/Sources/countly_flutter_np/include/countly_flutter_np/CountlyFlutterPlugin.h'
s.resource_bundles = { 'countly_flutter_np_privacy' => ['countly_flutter_np/Sources/countly_flutter_np/countly-sdk-ios/PrivacyInfo.xcprivacy'] }
s.dependency 'Flutter'
s.swift_version = '5.0'
s.ios.deployment_target = '10.0'
Expand Down
69 changes: 66 additions & 3 deletions scripts/script.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@
FILES_TO_ERASE = [
'../android/src/main/java/ly/count/dart/countly_flutter/CountlyMessagingService.java',
'../ios/countly_flutter.podspec',
'../ios/Classes/CountlyFLPushNotifications.h',
'../ios/Classes/CountlyFLPushNotifications.m'
'../ios/countly_flutter/Sources/countly_flutter/CountlyFLPushNotifications.h',
'../ios/countly_flutter/Sources/countly_flutter/CountlyFLPushNotifications.m'
] # array of string values. Relative path to the files. Something like: 'android/sth/sth.txt'
FILES_TO_MOVE = [
[
Expand Down Expand Up @@ -52,7 +52,7 @@
] # array of, arrays of string tuples. Relative path to the file and the relative path to the copy directory. Something like ['android/sth/sth.txt','android2/folder']
# paths to modify
modPathAndroid = '../android/src/main/java/ly/count/dart/countly_flutter/CountlyFlutterPlugin.java'
modPathIos = '../ios/Classes/CountlyFlutterPlugin.m'
modPathIos = '../ios/countly_flutter/Sources/countly_flutter/CountlyFlutterPlugin.m'
modPathCountly = '../lib/src/countly_flutter.dart'
modPathExampleYaml = '../example/pubspec.yaml'
# paths to change packages
Expand Down Expand Up @@ -211,6 +211,67 @@ def update_package(directory_path, from_package, to_package):

print(f'File processed: {file_path}')

# Renames the iOS Swift Package Manager package from countly_flutter to countly_flutter_np.
# Flutter discovers a plugin's SwiftPM package at ios/<package_name>/Package.swift, and the
# np flavor's package name is countly_flutter_np, so the package directory, target directory,
# public-header directory, and the names inside Package.swift must all be renamed to match.
# The Objective-C class (CountlyFlutterPlugin) and pluginClass in pubspec are unchanged.
def renameIosForNp(cwd):
iosDir = os.path.join(cwd, '../ios')
oldPkg = os.path.join(iosDir, 'countly_flutter')
newPkg = os.path.join(iosDir, 'countly_flutter_np')
if not os.path.exists(oldPkg):
print('iOS SwiftPM package dir not found, skipping rename:', oldPkg)
return

# Rename the inner target dir and its public-header dir before renaming the package dir.
srcOld = os.path.join(oldPkg, 'Sources', 'countly_flutter')
incOld = os.path.join(srcOld, 'include', 'countly_flutter')
if os.path.exists(incOld):
os.rename(incOld, os.path.join(srcOld, 'include', 'countly_flutter_np'))
if os.path.exists(srcOld):
os.rename(srcOld, os.path.join(oldPkg, 'Sources', 'countly_flutter_np'))

# Rewrite the SwiftPM manifest names (package, target, product/library, header search path).
pkgSwift = os.path.join(oldPkg, 'Package.swift')
if os.path.exists(pkgSwift):
with open(pkgSwift, 'r') as f:
content = f.read()
content = content.replace('name: "countly_flutter"', 'name: "countly_flutter_np"')
content = content.replace('["countly_flutter"]', '["countly_flutter_np"]')
content = content.replace('name: "countly-flutter"', 'name: "countly-flutter-np"')
content = content.replace('include/countly_flutter"', 'include/countly_flutter_np"')
with open(pkgSwift, 'w') as f:
f.write(content)
print('Rewrote Package.swift names for countly_flutter_np')

# The example app's AppDelegate imports the plugin module by name; the np pod's module
# is countly_flutter_np. (GeneratedPluginRegistrant is regenerated by flutter pub get.)
appDelegate = os.path.join(cwd, '../example/ios/Runner/AppDelegate.swift')
if os.path.exists(appDelegate):
with open(appDelegate, 'r') as f:
ad = f.read()
ad = ad.replace('import countly_flutter\n', 'import countly_flutter_np\n')
with open(appDelegate, 'w') as f:
f.write(ad)
print('Updated example AppDelegate import for countly_flutter_np')

# The example app's Notification Service Extension target references the vendored
# CountlyNotificationService files by path into the plugin; repoint it at the renamed dir.
pbxproj = os.path.join(cwd, '../example/ios/Runner.xcodeproj/project.pbxproj')
if os.path.exists(pbxproj):
with open(pbxproj, 'r') as f:
pbx = f.read()
pbx = pbx.replace('ios/countly_flutter/Sources/countly_flutter/',
'ios/countly_flutter_np/Sources/countly_flutter_np/')
with open(pbxproj, 'w') as f:
f.write(pbx)
print('Updated example NSE references for countly_flutter_np')

# Finally rename the package directory itself.
os.rename(oldPkg, newPkg)
print('Renamed iOS SwiftPM package directory to countly_flutter_np')

def main():
# give info about set constants
print('Paths to erase:')
Expand Down Expand Up @@ -253,6 +314,8 @@ def main():
update_package(packagePathLib, packagePrefix, packagePrefixToChange)
update_package(packagePathLibInternal, packagePrefix, packagePrefixToChange)
update_package(packagePathIntegrationTest, packagePrefix, packagePrefixToChange)
# rename the iOS SwiftPM package to match the np package name
renameIosForNp(cwd)
print('Done')
else:
print('Aborted')
Expand Down
10 changes: 5 additions & 5 deletions scripts/sync_sdk_versions.dart
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,10 @@ void main(List<String> args) {
);

replaceInFile(
'$rootDir/ios/Classes/CountlyFlutterPlugin.m',
'$rootDir/ios/countly_flutter/Sources/countly_flutter/CountlyFlutterPlugin.m',
RegExp(r'kCountlyFlutterSDKVersion = @".+"'),
'kCountlyFlutterSDKVersion = @"$flutterVersion"',
'Flutter → ios/Classes/CountlyFlutterPlugin.m',
'Flutter → ios/countly_flutter/Sources/countly_flutter/CountlyFlutterPlugin.m',
);

replaceInFile(
Expand Down Expand Up @@ -123,7 +123,7 @@ void main(List<String> args) {

// ---- iOS: submodule init & sparse checkout ----
final iosTag = args.isNotEmpty ? args[0] : iosVersion;
final submodulePath = 'ios/Classes/countly-sdk-ios';
final submodulePath = 'ios/countly_flutter/Sources/countly_flutter/countly-sdk-ios';
final sparseFile = '$scriptDir/config/sparse-checkout.list';

print('');
Expand Down Expand Up @@ -172,13 +172,13 @@ void main(List<String> args) {
'pubspec.yaml',
'ios/countly_flutter.podspec',
'android/src/main/java/ly/count/dart/countly_flutter/CountlyFlutterPlugin.java',
'ios/Classes/CountlyFlutterPlugin.m',
'ios/countly_flutter/Sources/countly_flutter/CountlyFlutterPlugin.m',
'lib/src/web/plugin_config.dart',
'scripts/no-push-files/pubspec.yaml',
'scripts/no-push-files/countly_flutter_np.podspec',
'android/build.gradle',
'scripts/no-push-files/build.gradle',
'ios/Classes/countly-sdk-ios',
'ios/countly_flutter/Sources/countly_flutter/countly-sdk-ios',
],
rootDir);
print('✅ All changed files staged');
Expand Down