Jak wyeksportować „gruby” Cocoa Touch Framework (dla symulatora i urządzenia)?

107

Dzięki Xcode 6 otrzymujemy możliwość tworzenia własnego Dynamic Cocoa Frameworks.

wprowadź opis obrazu tutaj

Z powodu:

  • Symulator nadal korzysta z 32-bitbiblioteki

  • od 1 czerwca 2015 r. aktualizacje aplikacji przesyłane do App Store muszą obejmować obsługę wersji 64-bitowej i być skompilowane przy użyciu zestawu SDK systemu iOS 8 ( developer.apple.com )

Musimy zrobić bibliotekę tłuszczu, aby uruchomić projekt na urządzeniach i symulatorach. tj. obsługuje zarówno 32, jak i 64 bitowe w Frameworkach.

Ale nie znalazłem żadnych podręczników, jak wyeksportować uniwersalny fat Framework do przyszłej integracji z innymi projektami (i udostępnić komuś tę bibliotekę).

Oto moje kroki, aby odtworzyć:

  1. Ustaw ONLY_ACTIVE_ARCH=NOwBuild Settings

    wprowadź opis obrazu tutaj

  2. Dodaj wsparcie armv7 armv7s arm64 i386 x86_64dla Architectures(na pewno)

wprowadź opis obrazu tutaj

  1. Zbuduj Framework i otwórz go w Finderze:

wprowadź opis obrazu tutaj wprowadź opis obrazu tutaj

  1. Dodaj tę platformę do innego projektu

Aktualny rezultat:

Ale w końcu nadal mam problem z uruchomieniem projektu z tym frameworkiem na urządzeniach i symulatorze jednocześnie.

  • jeśli pobiorę framework z Debug-iphoneosfolderu - działa na urządzeniach i wyświetla błąd na symulatorach:ld: symbol(s) not found for architecture i386

      xcrun lipo -info CoreActionSheetPicker

    Architektury w grubym pliku: CoreActionSheetPicker to: armv7 armv7s arm64

  • jeśli wezmę framework z Debug-iphonesimulatorfolderu - działa na symulatorach. i mam błąd na urządzeniu:ld: symbol(s) not found for architecture arm64

      xcrun lipo -info CoreActionSheetPicker

    Architektury w grubym pliku: CoreActionSheetPicker to: i386 x86_64

Jak więc stworzyć dynamiczną platformę działającą na urządzeniach i symulatorach?

Ta odpowiedź dotyczyła problemów z Xcode 6 iOS Creating a Cocoa Touch Framework - Architectures, ale nie jest to duplikat.


Aktualizacja:

Znalazłem "brudny hack" dla tej sprawy. Zobacz moją odpowiedź poniżej . Jeśli ktoś zna wygodniejszy sposób - daj mi znać!

skywinder
źródło
duplicate issue stackoverflow.com/questions/24039470/…
Andrius Steponavičius
@ AndriusSteponavičius to pytanie zostało zadane 2 miesiące wcześniej.
skywinder,
Tak, ale są tam znacznie bardziej szczegółowe odpowiedzi, o których myślę, że użytkownicy powinni wiedzieć
Andrius Steponavičius
Ustawienie ONLY_ACTIVE_ARCH = NO w ustawieniach kompilacji to ważny krok.
Jedidja
Twój framework potrzebuje obu wycinków i386 x86_64 w pliku binarnym Fat, jeśli chcesz go uruchomić na symulatorze NAWET JEŚLI TWÓJ KOMPUTER MA 64-BITOWĄ ARCHITEKTURĘ !!! Nauczyłem się tego na własnej skórze.
J.beenie

Odpowiedzi:

82

Aktualność tej odpowiedzi brzmi: lipiec 2015 r. Najprawdopodobniej sytuacja się zmieni.

TLDR;

Obecnie Xcode nie posiada narzędzi do automatycznego eksportu uniwersalnego frameworka fat, więc programista musi uciekać się do ręcznego użycia liponarzędzia. Również zgodnie z tym radarem przed przesłaniem do programisty AppStore, który jest konsumentem frameworka, również musi użyć lipodo usunięcia fragmentów symulatora z frameworka.

Następuje dłuższa odpowiedź


Zrobiłem podobne badania w temacie (link u dołu odpowiedzi).

I nie znalazł żadnej oficjalnej dokumentacji na temat dystrybucji więc moje badania oparto na eksploracji Apple Developer Forum projektów Carthage i kraina i moich własnych doświadczeń z xcodebuild, lipo, codesignnarzędzi.

Oto długi cytat (z odrobiną mojego znacznika) z wątku Apple Developer Forums Eksportowanie aplikacji z osadzoną strukturą :

Jaki jest właściwy sposób wyeksportowania frameworka z projektu frameworka?

Obecnie jedynym sposobem jest dokładnie to, co zrobiłeś:

  • Zbuduj cel zarówno dla symulatora, jak i urządzenia iOS.
  • Przejdź do folderu DerivedData Xcode dla tego projektu i połącz razem dwa pliki binarne w jedną strukturę. Jednak podczas budowania celu struktury w Xcode należy dostosować ustawienie docelowe „Tylko buduj aktywną architekturę” na „NIE”. Umożliwi to Xcode zbudowanie celu dla wielu typów binarty (arm64, armv7 itp.). To dlatego działa z Xcode, ale nie jako samodzielny plik binarny.

  • Będziesz także chciał upewnić się, że schemat jest ustawiony na kompilację wydania i skompilować obiekt docelowy platformy względem wydania. Jeśli nadal otrzymujesz błąd nie załadowanej biblioteki, sprawdź fragmenty kodu w ramach.

  • Użyj lipo -info MyFramworkBinaryi sprawdź wynik.

lipo -info MyFrameworkBinary

Wynik jest i386 x86_64 armv7 arm64

  • Nowoczesne uniwersalne frameworki będą zawierać 4 wycinki, ale mogą zawierać więcej: i386 x86_64 armv7 arm64 jeśli nie widzisz co najmniej tych 4, może to być spowodowane ustawieniem Build Active Architecture.

Opisuje to prawie tak samo, jak @skywinder zrobił to w swojej odpowiedzi.

W ten sposób Carthage używa lipo, a Realm używa lipo .


WAŻNE SZCZEGÓŁY

Jest radar: Xcode 6.1.1 i 6.2: frameworki iOS zawierające wycinki symulatora nie mogą być przesyłane do App Store i długa dyskusja na ten temat w Realm # 1163 i Carthage # 188, która zakończyła się specjalnym obejściem:

Przed przesłaniem do AppStore pliki binarne platformy iOS należy usunąć z wycinków symulatora

Carthage ma specjalny kod: CopyFrameworks i odpowiednią dokumentację:

Ten skrypt działa w przypadku błędu przesyłania w sklepie App Store wywołanego przez uniwersalne pliki binarne.

Realm ma specjalny skrypt: strip-frameworks.sh i odpowiednią dokumentację:

Ten krok jest wymagany do obejścia błędu przesyłania do sklepu App Store podczas archiwizowania uniwersalnych plików binarnych.

Jest też dobry artykuł: Usuwanie niechcianych architektur z bibliotek dynamicznych w Xcode .

Sam korzystałem z Realm, strip-frameworks.shktóry działał dla mnie doskonale bez żadnych modyfikacji, chociaż oczywiście każdy może napisać od podstaw.


Link do mojego tematu, który polecam przeczytać, ponieważ zawiera kolejny aspekt tego pytania: podpisywanie kodu - tworzenie frameworków iOS / OSX: czy konieczne jest kodowanie ich przed dystrybucją do innych programistów?

Stanislav Pankevich
źródło
1
Użyłem lipo, ale kiedy framework buduje się w symulatorze, pokazuje nierozwiązany identyfikator z nazwą klasy, ale w urządzeniu działa. Jeśli używasz wersji binarnej symulatora, to działa ... jakikolwiek pomysł?
Susim Samanta,
2
Nie znalazłem żadnych dowodów na to, że zmieniło się to przez Xcode 8.2 w grudniu 2016.: /
Geoffrey Wiseman
1
@Geoffrey, czy zmieniło się to w Xcode 9.2, czy jest coś innego? Po raz pierwszy tworzę binarny framework do dystrybucji i już się boję ...
ScottyB
Nie robiłem tego od jakiegoś czasu, niestety - nie mogę powiedzieć. Powodzenia.
Geoffrey Wiseman
57

To nie jest takie jasne rozwiązanie, ale jest jedyny sposób, który znajduję:

  1. Ustaw ONLY_ACTIVE_ARCH=NOwBuild Settings

    • Zbuduj bibliotekę symulatora
    • Zbuduj bibliotekę dla urządzenia
  2. Otwórz w Productsfolderze konsoli dla twojego frameworka (możesz go otworzyć, otwierając folder frameworka i cd ..stamtąd)

wprowadź opis obrazu tutaj wprowadź opis obrazu tutaj

  1. Uruchom ten skrypt z Productsfolderu. Tworzy gruby Framework w tym folderze. (lub zrób to ręcznie, jak wyjaśniono poniżej w 3. 4. )

Lub:

  1. Połącz te 2 frameworki za pomocą lipo za pomocą tego skryptu (zamień YourFrameworkNamena nazwę swojego Framework)

    lipo -create -output "YourFrameworkName" "Debug-iphonesimulator/YourFrameworkName.framework/YourFrameworkName" "Debug-iphoneos/YourFrameworkName.framework/YourFrameworkName"
  2. Zastąp nową binarną jedną z istniejących frameworków:

    cp -R Debug-iphoneos/YourFrameworkName.framework ./YourFrameworkName.framework
    mv YourFrameworkName ./YourFrameworkName.framework/YourFrameworkName

  1. Zysk: ./YourFrameworkName.framework- to gotowy do użycia plik binarny tłuszczu! Możesz go zaimportować do swojego projektu!

W przypadku projektu, którego nie ma w obszarach roboczych:

Możesz również spróbować wykorzystać tę istotę, jak opisano tutaj . Ale wygląda na to, że nie działa to w przypadku projektów w obszarach roboczych.

skywinder
źródło
Myślałem, że Apple nie akceptuje już grubych plików binarnych. kodmunki.wordpress.com/2015/03/04/…
Monstieur
1
@skywinder Czy znalazłeś inny prosty sposób wyeksportowania Cocoa Touch Framework do gotowego do użycia pliku binarnego tłuszczu? Używam tego samego podejścia co powyżej, ale nie podoba mi się to. Xcode powinien mieć taki, który automatyzuje proces.
dev gr
1
@devgr jeszcze nie ... dlatego nie zaakceptowałem własnej odpowiedzi. Wciąż szukam lepszego rozwiązania.
Skywinder
1
Nie można uruchomić w symulatorze, ale działa na urządzeniu z krokami 3 i 4
jose920405
1
@ Czy ktoś mógłby wyjaśnić, dlaczego Debug-używany jest tylko folder lipo -create? Czy ta struktura może być używana do Releasekonfiguracji i dlaczego? Dzięki.
Yevhen Dubinin
10

Odpowiedź @Stainlav była bardzo pomocna, ale zamiast tego skompilowałem dwie wersje frameworka (jedną dla urządzenia i jedną dla symulatora), a następnie dodałem następujące, Run Script Phaseaby automatycznie skopiować wstępnie skompilowany framework wymagany dla działającej architektury

echo "Copying frameworks for architecture: $CURRENT_ARCH"
if [ "${CURRENT_ARCH}" = "x86_64" ] || [ "${CURRENT_ARCH}" = "i386" ]; then
  cp -af "${SRCROOT}/Frameworks/Simulator/." "${SRCROOT}/Frameworks/Active"
else
  cp -af "${SRCROOT}/Frameworks/Device/." "${SRCROOT}/Frameworks/Active"
fi

W ten sposób nie muszę lipotworzyć grubego frameworka ani Realm strip-frameworks.shdo usuwania niepotrzebnych wycinków podczas przesyłania do App Store.

odm
źródło
Którego łączysz?
Jaka Jančar
@ JakaJančar I odsyłam do tych w ${SRCROOT}/Frameworks/Activefolderze. Są one zastępowane przez odpowiednie wstępnie skompilowane frameworki dla aktywnej architektury w czasie kompilacji.
odm
2
Kocham to! Jest to o wiele prostsze niż lipopodejście „ połącz, a następnie rozerwij” .
clozach
2

w zasadzie na to znalazłem bardzo dobre rozwiązanie. wystarczy wykonać te proste kroki.

  1. Utwórz strukturę dotykową kakao.
  2. Ustaw kod bitowy włączony na Nie.
  3. Wybierz cel i edytuj schematy. Wybierz Uruchom i wybierz Zwolnij z zakładki Informacje.
  4. Żadne inne ustawienie nie jest wymagane.
  5. Teraz skompiluj platformę dla dowolnego symulatora, ponieważ symulator działa w architekturze x86.
  6. Kliknij grupę Produkty w Nawigatorze projektu i znajdź plik .framework.
  7. Kliknij go prawym przyciskiem myszy i kliknij Pokaż w wyszukiwarce. Skopiuj i wklej go w dowolnym folderze, osobiście wolę nazwę „symulator”.
  8. Teraz utwórz platformę dla Generic iOS Device i wykonaj kroki od 6 do 9. Po prostu zmień nazwę folderu na „device” zamiast „simulator”.
  9. Skopiuj plik .framework urządzenia i wklej w dowolnym innym katalogu. Wolę bezpośredni super katalog obu. Tak więc struktura katalogów wygląda teraz następująco:
    • Pulpit
    • urządzenie
      • MyFramework.framework
    • symulator
      • MyFramework.framework
    • MyFramework.framework Teraz otwórz terminal i przejdź na pulpit. Teraz zacznij wpisywać następujące polecenie:

lipo -create 'urządzenie / MyFramework.framework / MyFramework' 'simulator / MyFramework.framework / MyFramework' -output 'MyFramework.framework / MyFramework'

i to wszystko. Tutaj łączymy symulator i wersję urządzenia binarnego MyFramework obecnego w MyFramework.framework. Otrzymujemy uniwersalny framework, który buduje się dla wszystkich architektur, w tym symulatora i urządzenia.

Nrip
źródło
Chcę utworzyć plik FAT z włączonym kodem bitowym. Proszę, prowadź mnie.
user3898700
2

Chcę tylko zaktualizować tę świetną odpowiedź przez @odm. Od Xcode 10 CURRENT_ARCHzmienna nie odzwierciedla już architektury kompilacji. Więc zmieniłem skrypt, żeby zamiast tego sprawdzić platformę:

echo "Copying frameworks for platform: $PLATFORM_NAME"
rm -R "${SRCROOT}/Frameworks/Active"
if [ "${PLATFORM_NAME}" = "iphonesimulator" ]; then
    cp -af "${SRCROOT}/Frameworks/Simulator/." "${SRCROOT}/Frameworks/Active"
else
    cp -af "${SRCROOT}/Frameworks/Device/." "${SRCROOT}/Frameworks/Active"
fi

Dodałem również linię, aby wyczyścić katalog docelowy przed skopiowaniem, ponieważ zauważyłem, że dodatkowe pliki w podkatalogach nie zostałyby w inny sposób nadpisane.

Dorian Roy
źródło
1

Moja odpowiedź obejmuje poniższe punkty:

  • Stwórz framework, który działa zarówno dla symulatora, jak i urządzenia

  • Jak wyeksportować „gruby” Cocoa Touch Framework (zarówno dla symulatora, jak i urządzenia)?

  • Niezdefiniowane symbole architektury x86_64

  • ld: nie znaleziono symboli dla architektury x86_64

Kroki 1: Najpierw zbuduj swoje frameworki za pomocą celu symulatora

Kroki 2: Po pomyślnym zakończeniu procesu tworzenia symulatora, teraz skompiluj swoją platformę z wyborem urządzenia docelowego lub ogólnym wyborem urządzenia iOS

Krok 3: Teraz wybierz docelowy framework iw tym celu w sekcji „Fazy kompilacji” wybierz „Dodaj skrypt uruchamiania” i skopiuj poniższy kod skryptu)

Krok 4: Teraz w końcu skompiluj ponownie, a twoja platforma jest gotowa zarówno na zgodność z symulatorem, jak i urządzeniem. Hurra!!!!

[Uwaga: musimy mieć obie kompatybilne ramy gotowe przed ostatnim krokiem 4 (kompatybilny z symulatorem i architekturą urządzenia, jeśli nie, postępuj zgodnie z powyższymi krokami 1 i 2)

Zobacz zdjęcie referencyjne:

wprowadź opis obrazu tutaj

wprowadź opis obrazu tutaj

Umieść poniższy kod w obszarze powłoki:

#!/bin/sh


UNIVERSAL_OUTPUTFOLDER=${BUILD_DIR}/${CONFIGURATION}-universal


# make sure the output directory exists

mkdir -p "${UNIVERSAL_OUTPUTFOLDER}"


# Step 1. Build Device and Simulator versions

xcodebuild -target "${PROJECT_NAME}" ONLY_ACTIVE_ARCH=NO -configuration ${CONFIGURATION} -sdk iphoneos  BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" clean build

xcodebuild -target "${PROJECT_NAME}" -configuration ${CONFIGURATION} -sdk iphonesimulator ONLY_ACTIVE_ARCH=NO BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" clean build


# Step 2. Copy the framework structure (from iphoneos build) to the universal folder

cp -R "${BUILD_DIR}/${CONFIGURATION}-iphoneos/${PROJECT_NAME}.framework" "${UNIVERSAL_OUTPUTFOLDER}/"


# Step 3. Copy Swift modules from iphonesimulator build (if it exists) to the copied framework directory

SIMULATOR_SWIFT_MODULES_DIR="${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${PROJECT_NAME}.framework/Modules/${PROJECT_NAME}.swiftmodule/."

if [ -d "${SIMULATOR_SWIFT_MODULES_DIR}" ]; then

cp -R "${SIMULATOR_SWIFT_MODULES_DIR}" "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework/Modules/${PROJECT_NAME}.swiftmodule"

fi


# Step 4. Create universal binary file using lipo and place the combined executable in the copied framework directory

lipo -create -output "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework/${PROJECT_NAME}" "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${PROJECT_NAME}.framework/${PROJECT_NAME}" "${BUILD_DIR}/${CONFIGURATION}-iphoneos/${PROJECT_NAME}.framework/${PROJECT_NAME}"


# Step 5. Convenience step to copy the framework to the project's directory

cp -R "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework" "${PROJECT_DIR}"


# Step 6. Convenience step to open the project's directory in Finder

open "${BUILD_DIR}/${CONFIGURATION}-universal"

Sandip Patel - SM
źródło
Ten skrypt wydaje się wywoływać siebie, powodując w ten sposób nieskończoną pętlę !! Musiałem ponownie uruchomić komputer po uruchomieniu! Ciągle tworzył nowe procesy xcodebuild ... i otwierał nowe okna wyszukiwarki
Głosowałbym w
Sprawdź odpowiedź @ l0gg3r w tym SO Q / A dla podobnego skryptu bez problemu rekursji.
J.beenie