Author SHA1 Message Date
NanoTech c8959b0cf2 Change some titles 2017-02-15 22:35:53 -06:00
NanoTech 16bb42eedd Update screenshots
- Crop the first two project creation screenshots
- Update and use the New Copy Files Phase screenshot
- Remove unused screenshots
2017-02-15 22:20:47 -06:00
NanoTech d185c5f0c2 Update Run Script input files 2017-02-15 22:10:50 -06:00
NanoTech 6f870aea9c Fix linking with -whole-module-optimization enabled 2017-02-15 22:10:50 -06:00
13 changed files with 55 additions and 36 deletions
+37 -26
View File
@@ -1,11 +1,12 @@
# Integrating Haskell with Swift Mac Apps # Integrating Haskell with Swift Mac Apps
To start, create a new Cocoa Application Xcode project To start, create a new Cocoa Application Xcode project
with Swift as the default language:
![Select the Cocoa app template](tutorial/xcode-cocoa-template.png) ![Select the Cocoa app template](tutorial/xcode-cocoa-template-icon.png)
![Create a new project](tutorial/xcode-create-project.png) with Swift as the default language.
![Select Swift as the default language](tutorial/xcode-create-project-language-swift.png)
Then `cd` into the directory with the `.xcodeproj` and create a Then `cd` into the directory with the `.xcodeproj` and create a
new stack project: new stack project:
@@ -195,6 +196,8 @@ phases, and add a new **Copy Files** phase that copies the
`SwiftHaskell` executable into the app bundle's Executables `SwiftHaskell` executable into the app bundle's Executables
directory: directory:
![New Copy Files Phase](tutorial/xcode-new-copy-files-phase.png)
![Copy into Executables](tutorial/xcode-copy-files-swifthaskell-executable.png) ![Copy into Executables](tutorial/xcode-copy-files-swifthaskell-executable.png)
Finally, in the SwiftAppLibrary framework target's Build Phases, Finally, in the SwiftAppLibrary framework target's Build Phases,
@@ -226,35 +229,42 @@ executable SwiftHaskell
executable where the dynamic linker should look for shared executable where the dynamic linker should look for shared
libraries. libraries.
## Starting the GUI ## Starting Cocoa
Because Haskell has control over the program's entry point Because Haskell has control over the program's entry point
(`main`), we'll need to have it call out to Swift to (`main`), we'll need to have it call out to Cocoa to start its
start Cocoa's main thread. Add this top level function to main thread. In `SwiftAppLibrary.h`, declare a new function
`AppDelegate.swift`: named `runNSApplication` and mark it as `FOUNDATION_EXPORT` to
indicate that it should be exported from the framework:
```swift
@_cdecl("runNSApplication")
func runNSApplication() {
let app = NSApplication.shared()
var topObjects: NSArray = []
NSNib.init(nibNamed: "MainMenu", bundle: Bundle(for: AppDelegate.self))!
.instantiate(withOwner: app, topLevelObjects: &topObjects)
app.run()
}
```
`@_cdecl` sets the symbol name of the Swift function, overriding
normal symbol mangling.
In `SwiftAppLibrary.h`, add a `FOUNDATION_EXPORT` C prototype
for the function, so that its symbol gets marked as public and
is exported from the framework:
```c ```c
FOUNDATION_EXPORT void runNSApplication(void); FOUNDATION_EXPORT void runNSApplication(void);
``` ```
Implement the function by adding a new Objective-C `.m` file to
the framework target containing
```objective-c
#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];
}
```
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.
In `Main.hs`, import the foreign function and call it from In `Main.hs`, import the foreign function and call it from
the end of `main`: the end of `main`:
@@ -286,7 +296,7 @@ from `MainMenu.xib`:
![A blank window](tutorial/empty-app.png) ![A blank window](tutorial/empty-app.png)
## Completing Linking Setup ## Linking to the Executable
Add `$(PROJECT_DIR)/SwiftHaskell/include` to the framework Add `$(PROJECT_DIR)/SwiftHaskell/include` to the framework
target's **Swift Compiler - Search Paths, Import Paths** setting target's **Swift Compiler - Search Paths, Import Paths** setting
@@ -335,6 +345,7 @@ Add the Haskell sources as input files:
``` ```
$(PROJECT_DIR)/src/Main.hs $(PROJECT_DIR)/src/Main.hs
$(PROJECT_DIR)/SwiftHaskellLibrary.cabal $(PROJECT_DIR)/SwiftHaskellLibrary.cabal
$(PROJECT_DIR)/stack.yaml
``` ```
And the executable as an output file: And the executable as an output file:
-9
View File
@@ -37,15 +37,6 @@ class AppDelegate: NSObject, NSApplicationDelegate {
} }
} }
@_cdecl("runNSApplication")
func runNSApplication() {
let app = NSApplication.shared()
var topObjects: NSArray = []
NSNib.init(nibNamed: "MainMenu", bundle: Bundle(for: AppDelegate.self))!
.instantiate(withOwner: app, topLevelObjects: &topObjects)
app.run()
}
// Swift to Haskell ByteString Example // Swift to Haskell ByteString Example
+13
View File
@@ -0,0 +1,13 @@
#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];
}
+5 -1
View File
@@ -13,6 +13,7 @@
BFABC4E01E4D781D006036C6 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = BFABC4DF1E4D781D006036C6 /* Assets.xcassets */; }; BFABC4E01E4D781D006036C6 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = BFABC4DF1E4D781D006036C6 /* Assets.xcassets */; };
BFABC4ED1E4D78E5006036C6 /* SwiftHaskell in Copy Files */ = {isa = PBXBuildFile; fileRef = BFABC4EA1E4D78D9006036C6 /* SwiftHaskell */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; BFABC4ED1E4D78E5006036C6 /* SwiftHaskell in Copy Files */ = {isa = PBXBuildFile; fileRef = BFABC4EA1E4D78D9006036C6 /* SwiftHaskell */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
BFABC4F01E4D7951006036C6 /* SwiftAppLibrary.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = BFABC45B1E4C1DD1006036C6 /* SwiftAppLibrary.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; BFABC4F01E4D7951006036C6 /* SwiftAppLibrary.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = BFABC45B1E4C1DD1006036C6 /* SwiftAppLibrary.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
BFE4CC421E55553000F232D6 /* SwiftAppLibrary.m in Sources */ = {isa = PBXBuildFile; fileRef = BFE4CC411E55553000F232D6 /* SwiftAppLibrary.m */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */ /* Begin PBXContainerItemProxy section */
@@ -60,6 +61,7 @@
BFABC4DF1E4D781D006036C6 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; }; BFABC4DF1E4D781D006036C6 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
BFABC4E41E4D781D006036C6 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; BFABC4E41E4D781D006036C6 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
BFABC4EA1E4D78D9006036C6 /* SwiftHaskell */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; name = SwiftHaskell; path = build/SwiftHaskell; sourceTree = SOURCE_ROOT; }; BFABC4EA1E4D78D9006036C6 /* SwiftHaskell */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; name = SwiftHaskell; path = build/SwiftHaskell; sourceTree = SOURCE_ROOT; };
BFE4CC411E55553000F232D6 /* SwiftAppLibrary.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SwiftAppLibrary.m; sourceTree = "<group>"; };
/* End PBXFileReference section */ /* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */ /* Begin PBXFrameworksBuildPhase section */
@@ -95,6 +97,7 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
BFABC45E1E4C1DD1006036C6 /* SwiftAppLibrary.h */, BFABC45E1E4C1DD1006036C6 /* SwiftAppLibrary.h */,
BFE4CC411E55553000F232D6 /* SwiftAppLibrary.m */,
BFABC4CC1E4D627E006036C6 /* AppDelegate.swift */, BFABC4CC1E4D627E006036C6 /* AppDelegate.swift */,
BFABC4D01E4D664D006036C6 /* MainMenu.xib */, BFABC4D01E4D664D006036C6 /* MainMenu.xib */,
BFABC45F1E4C1DD1006036C6 /* Info.plist */, BFABC45F1E4C1DD1006036C6 /* Info.plist */,
@@ -229,9 +232,9 @@
files = ( files = (
); );
inputPaths = ( inputPaths = (
"$(PROJECT_DIR)/src/Lib.hs",
"$(PROJECT_DIR)/src/Main.hs", "$(PROJECT_DIR)/src/Main.hs",
"$(PROJECT_DIR)/SwiftHaskellLibrary.cabal", "$(PROJECT_DIR)/SwiftHaskellLibrary.cabal",
"$(PROJECT_DIR)/stack.yaml",
); );
name = "stack build and link-deps"; name = "stack build and link-deps";
outputPaths = ( outputPaths = (
@@ -266,6 +269,7 @@
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
BFABC4CD1E4D627E006036C6 /* AppDelegate.swift in Sources */, BFABC4CD1E4D627E006036C6 /* AppDelegate.swift in Sources */,
BFE4CC421E55553000F232D6 /* SwiftAppLibrary.m in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 20 KiB