aws-amplify / aws-sdk-ios-spm

This repository enables Swift Package Manager support for the AWS Mobile SDK for iOS
Apache License 2.0
29 stars 16 forks source link

Mac Catalyst support #81

Closed floriansegginger closed 11 months ago

floriansegginger commented 1 year ago

The source repository (https://github.com/aws-amplify/aws-sdk-ios) already has support for Mac Catalyst.

It would be nice if this SPM package repository would also support Mac Catalyst out of the box. The source repository is very large!

atierian commented 1 year ago

Thanks for opening this issue @floriansegginger. Are you encountering a specific error or limitation that's preventing you from using Mac Catalyst with this package? I was able to run using Mac Catalyst with a simple hello world type application using AWS SDK for iOS via SPM. Any further information about the specific issue you're encountering would be helpful. Thanks!

floriansegginger commented 1 year ago

Sorry for not replying earlier.

I have created an empty "cross-platform" XCode project with Mac Catalyst support, added the aws-sdk-ios SPM package and the AWSCore and ASWS3 packages to the project.

When trying to build for Mac Catalyst, I get this type of error for all packages I added:

While building for macOS, no library for this platform was found in '/.../aws-sdk-ios-spm/AWSCore.xcframework'.

Here is a link to the project repository: https://github.com/floriansegginger/AWS-Catalyst-Test.

Note: you need to make sure to set the target to something like "My Mac (Mac Catalyst)" - obviously if you don't do that and try to build for iOS, it will work.

As it stands I still need to include the entire AWS SDK source repository to build my app for Mac Catalyst

atierian commented 1 year ago

Thanks for the info, I've reopened the issue. We'll investigate and update here.

rafaelks commented 1 year ago

@atierian Any updates on this? Right now AFAIK it's impossible to use this library with SPM on Catalyst. CocoaPods works fine.

atierian commented 1 year ago

Thanks for checking in; I don't have any updates at this time. We'll make sure to update here if that changes.

atierian commented 11 months ago

We recommend using Amplify Swift for building applications that run on multiple device types. Amplify Swift currently supports iOS, macOS, tvOS, and watchOS -- it also has preview support for visionOS.

For more information on Amplify Swift, please check out the documentation and the Amplify Swift repo.

We don't plan on adding Mac Catalyst support to the AWS SDK for iOS at this time. If Amplify Swift isn't an option for you (we'd love to hear why), here's an overview of how you can create XCFrameworks containing a Mac Catalyst slice.

  1. Clone /aws-amplify/aws-sdk-ios
  2. Update the file /CircleciScripts/create_xcframeworks.py as follows:
    create_xcframeworks.py
import os
import sys
import shutil

from multiprocessing import Pool
from framework_list import xcframeworks
from functions import log, run_command

PWD = os.getcwd()

IOS_DEVICE_ARCHIVE_PATH = f"{PWD}/xcframeworks/output/iOS/"
MAC_CATALYST_ARCHIVE_PATH = f"{PWD}/xcframeworks/output/macCatalyst/"
IOS_SIMULATOR_ARCHIVE_PATH = f"{PWD}/xcframeworks/output/Simulator/"
XCFRAMEWORK_PATH = f"{PWD}/xcframeworks/output/XCF/"

def create_archive(framework, project_file, archive_path, destination):
    cmd = [
        "xcodebuild",
        "archive",
        "-project",
        project_file,
        "-scheme",
        framework,
        "-destination",
        destination,
        "-archivePath",
        archive_path,
        "SKIP_INSTALL=NO",
        "BUILD_LIBRARY_FOR_DISTRIBUTION=YES"
    ]

    (exit_code, out, err) = run_command(cmd, keepalive_interval=300, timeout=7200)
    if exit_code == 0:
        log(f"Created archive for framework: {framework} with destination: {destination}")
    else:
        log(f"Could not create xcodebuild archive: {framework} output: {out}; error: {err}")
        sys.exit(exit_code)

def map_framework_to_project(framework_list):
    framework_map = {}
    cmd = [
        "xcodebuild",
        "-project",
        "AWSiOSSDKv2.xcodeproj",
        "-list",
    ]
    (exit_code, out, err) = run_command(cmd, keepalive_interval=300, timeout=7200)
    if exit_code == 0:
        log(f"List of schema found")
    else:
        log(f"Xcodebuild list failed: output: {out}; error: {err}")
        sys.exit(exit_code)

    for framework in framework_list:
        if framework not in str(out):
            framework_map[framework] = "./AWSAuthSDK/AWSAuthSDK.xcodeproj"
        else:
            framework_map[framework] = "AWSiOSSDKv2.xcodeproj"
    return framework_map

def archive(framework):
    xcframework = f"{XCFRAMEWORK_PATH}{framework}.xcframework"

    if os.path.exists(xcframework):
        log(f"skipping {framework}...")
        return

    log(f"Creating archives for {framework}")

    create_archive(framework=framework, project_file=framework_map[framework], archive_path=f"{IOS_DEVICE_ARCHIVE_PATH}{framework}", destination="generic/platform=iOS")
    create_archive(framework=framework, project_file=framework_map[framework],  archive_path=f"{MAC_CATALYST_ARCHIVE_PATH}{framework}", destination="platform=macOS,variant=Mac Catalyst")
    create_archive(framework=framework, project_file=framework_map[framework],  archive_path=f"{IOS_SIMULATOR_ARCHIVE_PATH}{framework}", destination="generic/platform=iOS Simulator")

framework_map = map_framework_to_project(xcframeworks)

def create_xc_framework(framework):
    ios_device_framework = f"{IOS_DEVICE_ARCHIVE_PATH}{framework}.xcarchive/Products/Library/Frameworks/{framework}.framework"
    ios_device_debug_symbols = f"{IOS_DEVICE_ARCHIVE_PATH}{framework}.xcarchive/dSYMs/{framework}.framework.dSYM"
    ios_simulator_framework = f"{IOS_SIMULATOR_ARCHIVE_PATH}{framework}.xcarchive/Products/Library/Frameworks/{framework}.framework"
    ios_simulator_debug_symbols = f"{IOS_SIMULATOR_ARCHIVE_PATH}{framework}.xcarchive/dSYMs/{framework}.framework.dSYM"
    mac_catalyst_framework = f"{MAC_CATALYST_ARCHIVE_PATH}{framework}.xcarchive/Products/Library/Frameworks/{framework}.framework"
    mac_catalyst_debug_symbols = f"{MAC_CATALYST_ARCHIVE_PATH}{framework}.xcarchive/dSYMs/{framework}.framework.dSYM"
    xcframework = f"{XCFRAMEWORK_PATH}{framework}.xcframework"
    if os.path.exists(xcframework):
        log(f"skipping {framework}...")
    else:
        log(f"Creating XCF for {framework}")

        cmd = [
                "xcodebuild",
                "-create-xcframework",
                "-framework",
                ios_device_framework,
                "-debug-symbols",
                ios_device_debug_symbols,
                "-framework",
                ios_simulator_framework,
                "-debug-symbols",
                ios_simulator_debug_symbols,
                "-framework",
                mac_catalyst_framework,
                "-debug-symbols",
                mac_catalyst_debug_symbols,
                "-output",
                xcframework
            ]
        (exit_code, out, err) = run_command(cmd, keepalive_interval=300, timeout=7200)
        if exit_code == 0:
            log(f"Created XCFramework for {framework}")
        else:
            log(f"Could not create XCFramework: {framework} output: {out}; error: {err}")
            sys.exit(exit_code)

def process_frameworks(process, base_frameworks, remaining_frameworks, pool):
    for base_framework in base_frameworks:
        process(base_framework)

    with pool:
        pool.map(process, remaining_frameworks)

if __name__ == '__main__':
    project_dir = os.getcwd()
    log(f"Creating XCFrameworks in {project_dir}")

    # These base_frameworks must be built first and serially.
    # Other frameworks are dependant on these base_frameworks,
    # and the build scripts will fail if they're not archived /
    # created in the proper order.
    # *** This may need to be updated when dependencies change ***
    base_frameworks = xcframeworks[:5]

    # The remaining_frameworks are archived and built concurrently
    # because they are dependent only on base_frameworks.
    remaining_frameworks = xcframeworks[5:]

    # To build the remaining_frameworks concurrently, we're using
    # a Pool(). By omitting an explicit input into the Pool constructor
    # we're allowing it to decide an appropriate amount of processes to run.
    archive_pool = Pool()

    # First let's archive everything.
    process_frameworks(archive, base_frameworks, remaining_frameworks, archive_pool)

    create_frameworks_pool = Pool()
    # Next let's create the xcframeworks.
    process_frameworks(create_xc_framework, base_frameworks, remaining_frameworks, create_frameworks_pool)

    if os.path.exists(IOS_DEVICE_ARCHIVE_PATH):
        shutil.rmtree(IOS_DEVICE_ARCHIVE_PATH)
    if os.path.exists(IOS_SIMULATOR_ARCHIVE_PATH):
        shutil.rmtree(IOS_SIMULATOR_ARCHIVE_PATH)

  1. Run python3 ./CircleciScripts/create_xcframeworks.py
  2. Wait for XCFrameworks to be generated under ./xcframeworks/output/XCF/
  3. Add generated frameworks to your project as needed