swift-haskell-tutorial icon indicating copy to clipboard operation
swift-haskell-tutorial copied to clipboard

What aspects of the tutorial are different if creating an iOS app?

Open psandovalsegura opened this issue 7 years ago • 5 comments

I am trying to build an iOS app in Swift, using a Haskell backend for logic. There have been no comprehensive tutorials online about how to do this, so I assumed that it wouldn't be too different from creating a Mac app. I did find one repo called ghc-ios, which had a guide on how to set up a iOS project, but ghc-ios was written in 2014 when Swift was still in its early days (in addition, Cabal linking is a mess in that tutorial and did not work).

I was able to get through half of the tutorial*, up until I am supposed to "Build the SwiftHaskellLibrary framework in Xcode, then stack build, and then finally build and run the SwiftHaskell app target to launch the app and see the default window from MainMenu.xib".

*At one point, we assumed MainMenu.xib could be considered the same as Main.storyboard?

Then, during stack build we get the following error:

ld: warning: URGENT: building for OSX, but linking against dylib (build/SwiftAppLibrary.framework/SwiftAppLibrary) built for iOS. Note: This will be an error in the future.
Undefined symbols for architecture x86_64:
  "_runNSApplication", referenced from:
      _r2YD_info in Main.o
      _c30S_info in Main.o
     (maybe you meant: _Main_runNSApplication_info, _Main_runNSApplication_closure )
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

Does anyone know what modifications I could follow to properly run Haskell code in Swift (3.0.2) for iOS? Is it even possible to make modifications to this Mac tutorial?

Any insight would be helpful. Thank you.

psandovalsegura avatar Mar 16 '17 03:03 psandovalsegura

You'll still need ghc-ios or an equivalent cross-compiling GHC, as a standard x86_64 GHC can't produce programs for iOS ARM (and maybe not even the simulator). ghc-ios might have been started in 2014, but it looks like it's been updated to keep pace with iOS updates. Changes to Swift can't really break ghc-ios, as ghc-ios doesn't depend on Swift.

Main.storyboard is indeed the equivalent of MainMenu.xib.

Regarding your error message, if the linker is not finding the symbol because of the platform mismatch, then you'll need to configure stack to use ghc-ios. (This would be needed anyways to get an ARM binary.) It might also be that the symbol is just not in the library; you can check with nm -a.

I don't know whether the full linking setup in the tutorial will actually work on an iOS device with all its signing and execution restrictions, but I think the platforms are similar enough that it's plausible that it could work.

nanotech avatar Mar 20 '17 23:03 nanotech

I assume that runNSApplication is different for storyboard? at least that one did not work in a storyboard project

Verneri avatar Apr 05 '18 03:04 Verneri

Yeah, need to use the contents of main.m from an iOS app instead. Something like (not tested):

void runNSApplication(void) {
    int argc = 1;
    char *argv[] = {strdup("app")};
    @autoreleasepool {
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}

I don't remember why I wasn't using NSApplicationMain in the tutorial project's code, so there might be some subtlety that would require loading the storyboard and starting UIApplication yourself.

nanotech avatar Apr 12 '18 01:04 nanotech

Hi, I wonder could you help with this:

When you write:

This is possible to write in Swift, however as of Swift 3.0.2, the annotation to export unmangled C symbols (@_cdecl) is not documented as stable. Additionally, whole module optimization will assume that @_cdecl symbols are unused and remove them.

Has this changed in later versions of Swift? And would it be possible to rewrite this to work with Storyboards, or even better SwiftUI? Thanks!

#import "SwiftAppLibrary.h"

@interface AClassInThisFramework : NSObject @end
@implementation AClassInThisFramework @end


void runNSApplication(void) {
    NSApplication *app = [NSApplication sharedApplication];
    NSBundle *bundle = [NSBundle bundleForClass:[AClassInThisFramework class]];
    NSArray *topObjects;
    [[[NSNib alloc] initWithNibNamed:@"MainMenu" bundle:bundle]
     instantiateWithOwner:app topLevelObjects:&topObjects];
    [app run];
}

iboy avatar May 17 '20 15:05 iboy

@_cdecl does still work and has tests in the Swift compiler repo. I'm not sure about its stability status or if the bug with whole module optimization has been fixed.

Storyboards and Swift UI may work the same if launching with UIApplicationMain. Here's an example in Swift 5:

@_cdecl("runUIApplication")
func runUIApplication() {
    let argc: Int32 = 1
    let argv = UnsafeMutablePointer<UnsafeMutablePointer<Int8>?>.allocate(capacity: 1)
    // Should probably pass the real arguments from Haskell's getArgs
    // or Foundation's ProcessInfo instead of this placeholder.
    argv[0] = strdup("app")
    return autoreleasepool {
        UIApplicationMain(argc, argv, nil, NSStringFromClass(AppDelegate.self))
    }
}

You'll also need to remove @UIApplicationMain from your AppDelegate, just like removing @NSApplicationMain for macOS in the tutorial.

nanotech avatar May 18 '20 05:05 nanotech