From d47927457b9c6c383e64431706ef6bc592f8cb98 Mon Sep 17 00:00:00 2001 From: NiwakaDev Date: Sun, 2 Jun 2024 16:29:35 +0900 Subject: [PATCH 1/4] Support failable initializers --- .../OptionTests.swift | 5 +++ crates/swift-bridge-ir/src/bridged_type.rs | 9 +++++ .../src/bridged_type/bridgeable_pointer.rs | 4 ++ .../src/bridged_type/bridgeable_string.rs | 4 ++ .../src/bridged_type/bridged_opaque_type.rs | 4 ++ .../src/bridged_type/built_in_tuple.rs | 4 ++ .../src/codegen/generate_swift.rs | 40 +++++++++++++++++++ .../generate_function_swift_calls_rust.rs | 16 +++++++- .../src/codegen/generate_swift/swift_class.rs | 1 + .../src/parse/parse_extern_mod.rs | 33 +++++++++++---- .../parse_extern_mod/function_attributes.rs | 21 ++++++++++ .../swift-bridge-ir/src/parsed_extern_fn.rs | 4 ++ crates/swift-integration-tests/src/option.rs | 21 ++++++++++ 13 files changed, 157 insertions(+), 9 deletions(-) diff --git a/SwiftRustIntegrationTestRunner/SwiftRustIntegrationTestRunnerTests/OptionTests.swift b/SwiftRustIntegrationTestRunner/SwiftRustIntegrationTestRunnerTests/OptionTests.swift index 7a64f602..ba3c5df8 100644 --- a/SwiftRustIntegrationTestRunner/SwiftRustIntegrationTestRunnerTests/OptionTests.swift +++ b/SwiftRustIntegrationTestRunner/SwiftRustIntegrationTestRunnerTests/OptionTests.swift @@ -220,4 +220,9 @@ class OptionTests: XCTestCase { XCTAssertEqual(reflectedSome!.field, 123) XCTAssertNil(reflectedNone) } + + func testFailableInitializer() { + XCTAssertEqual(FailableInitType(false), nil) + XCTAssertNotEqual(FailableInitType(true), nil) + } } diff --git a/crates/swift-bridge-ir/src/bridged_type.rs b/crates/swift-bridge-ir/src/bridged_type.rs index 3e58309b..6b423f85 100644 --- a/crates/swift-bridge-ir/src/bridged_type.rs +++ b/crates/swift-bridge-ir/src/bridged_type.rs @@ -84,6 +84,8 @@ pub(crate) trait BridgeableType: Debug { fn as_result(&self) -> Option<&BuiltInResult>; + fn as_option(&self) -> Option<&BridgedOption>; + /// True if the type's FFI representation is a pointer fn is_passed_via_pointer(&self) -> bool; @@ -494,6 +496,13 @@ impl BridgeableType for BridgedType { } } + fn as_option(&self) -> Option<&BridgedOption> { + match self { + BridgedType::StdLib(StdLibType::Option(ty)) => Some(ty), + _ => None, + } + } + fn is_passed_via_pointer(&self) -> bool { match self { BridgedType::StdLib(StdLibType::Vec(_)) => true, diff --git a/crates/swift-bridge-ir/src/bridged_type/bridgeable_pointer.rs b/crates/swift-bridge-ir/src/bridged_type/bridgeable_pointer.rs index a8e17cbb..4c90cb9c 100644 --- a/crates/swift-bridge-ir/src/bridged_type/bridgeable_pointer.rs +++ b/crates/swift-bridge-ir/src/bridged_type/bridgeable_pointer.rs @@ -41,6 +41,10 @@ impl BridgeableType for BuiltInPointer { todo!() } + fn as_option(&self) -> Option<&super::bridged_option::BridgedOption> { + todo!(); + } + fn is_passed_via_pointer(&self) -> bool { todo!() } diff --git a/crates/swift-bridge-ir/src/bridged_type/bridgeable_string.rs b/crates/swift-bridge-ir/src/bridged_type/bridgeable_string.rs index f151115b..b5bca782 100644 --- a/crates/swift-bridge-ir/src/bridged_type/bridgeable_string.rs +++ b/crates/swift-bridge-ir/src/bridged_type/bridgeable_string.rs @@ -26,6 +26,10 @@ impl BridgeableType for BridgedString { None } + fn as_option(&self) -> Option<&super::bridged_option::BridgedOption> { + todo!() + } + fn is_passed_via_pointer(&self) -> bool { true } diff --git a/crates/swift-bridge-ir/src/bridged_type/bridged_opaque_type.rs b/crates/swift-bridge-ir/src/bridged_type/bridged_opaque_type.rs index ea979fa5..053fe6fe 100644 --- a/crates/swift-bridge-ir/src/bridged_type/bridged_opaque_type.rs +++ b/crates/swift-bridge-ir/src/bridged_type/bridged_opaque_type.rs @@ -36,6 +36,10 @@ impl BridgeableType for OpaqueForeignType { None } + fn as_option(&self) -> Option<&super::bridged_option::BridgedOption> { + None + } + fn is_passed_via_pointer(&self) -> bool { true } diff --git a/crates/swift-bridge-ir/src/bridged_type/built_in_tuple.rs b/crates/swift-bridge-ir/src/bridged_type/built_in_tuple.rs index 301049a7..bc28dbff 100644 --- a/crates/swift-bridge-ir/src/bridged_type/built_in_tuple.rs +++ b/crates/swift-bridge-ir/src/bridged_type/built_in_tuple.rs @@ -65,6 +65,10 @@ impl BridgeableType for BuiltInTuple { todo!(); } + fn as_option(&self) -> Option<&super::bridged_option::BridgedOption> { + todo!() + } + fn is_passed_via_pointer(&self) -> bool { todo!(); } diff --git a/crates/swift-bridge-ir/src/codegen/generate_swift.rs b/crates/swift-bridge-ir/src/codegen/generate_swift.rs index 491cd818..c8fbbe39 100644 --- a/crates/swift-bridge-ir/src/codegen/generate_swift.rs +++ b/crates/swift-bridge-ir/src/codegen/generate_swift.rs @@ -606,6 +606,46 @@ func __swift_bridge__Foo_new (_ a: UInt8) -> UnsafeMutableRawPointer { assert_trimmed_generated_contains_trimmed_expected(&generated, &expected); } + /// Verify that we generated a Swift class with a failable init method. + #[test] + fn class_with_failable_init() { + let tokens = quote! { + mod foo { + extern "Rust" { + type Foo; + + #[swift_bridge(init)] + fn new() -> Option; + } + } + }; + let module: SwiftBridgeModule = parse_quote!(#tokens); + let generated = module.generate_swift(&CodegenConfig::no_features_enabled()); + + let expected = r#" +public class Foo: FooRefMut { + var isOwned: Bool = true + + public override init(ptr: UnsafeMutableRawPointer) { + super.init(ptr: ptr) + } + + deinit { + if isOwned { + __swift_bridge__$Foo$_free(ptr) + } + } +} +extension Foo { + public convenience init?() { + guard let val = __swift_bridge__$Foo$new() else { return nil }; self.init(ptr: val) + } +} +"#; + + assert_trimmed_generated_contains_trimmed_expected(&generated, expected); + } + /// Verify that we generated a Swift class with an init method. #[test] fn class_with_init() { diff --git a/crates/swift-bridge-ir/src/codegen/generate_swift/generate_function_swift_calls_rust.rs b/crates/swift-bridge-ir/src/codegen/generate_swift/generate_function_swift_calls_rust.rs index fe698efd..a52a9933 100644 --- a/crates/swift-bridge-ir/src/codegen/generate_swift/generate_function_swift_calls_rust.rs +++ b/crates/swift-bridge-ir/src/codegen/generate_swift/generate_function_swift_calls_rust.rs @@ -55,7 +55,11 @@ pub(super) fn gen_func_swift_calls_rust( if function.is_copy_method_on_opaque_type() { "public init".to_string() } else { - "public convenience init".to_string() + if function.is_swift_failable_initializer { + "public convenience init?".to_string() + } else { + "public convenience init".to_string() + } } } else { if let Some(swift_name) = &function.swift_name_override { @@ -179,7 +183,14 @@ pub(super) fn gen_func_swift_calls_rust( if function.is_copy_method_on_opaque_type() { call_rust = format!("self.bytes = {}", call_rust) } else { - call_rust = format!("self.init(ptr: {})", call_rust) + if function.is_swift_failable_initializer { + call_rust = format!( + "guard let val = {} else {{ return nil }}; self.init(ptr: val)", + call_rust + ) + } else { + call_rust = format!("self.init(ptr: {})", call_rust) + } } } @@ -316,5 +327,6 @@ return{maybe_try}await {with_checked_continuation_function_name}({{ (continuatio call_rust = call_rust, ) }; + func_definition } diff --git a/crates/swift-bridge-ir/src/codegen/generate_swift/swift_class.rs b/crates/swift-bridge-ir/src/codegen/generate_swift/swift_class.rs index 3f083fbb..b2a30d51 100644 --- a/crates/swift-bridge-ir/src/codegen/generate_swift/swift_class.rs +++ b/crates/swift-bridge-ir/src/codegen/generate_swift/swift_class.rs @@ -233,6 +233,7 @@ extension {ty_name}Ref: Hashable{{ "".to_string() } }; + let class = format!( r#" {class_decl}{initializers}{owned_instance_methods}{class_ref_decl}{ref_mut_instance_methods}{class_ref_mut_decl}{ref_instance_methods}{generic_freer}{equatable_method}{hashable_method}"#, diff --git a/crates/swift-bridge-ir/src/parse/parse_extern_mod.rs b/crates/swift-bridge-ir/src/parse/parse_extern_mod.rs index 6e7c8148..b15c1cfd 100644 --- a/crates/swift-bridge-ir/src/parse/parse_extern_mod.rs +++ b/crates/swift-bridge-ir/src/parse/parse_extern_mod.rs @@ -111,14 +111,19 @@ impl<'a> ForeignModParser<'a> { } let return_type = &func.sig.output; + let mut is_swift_failable_initializer = false; if let ReturnType::Type(_, return_ty) = return_type { - if BridgedType::new_with_type(return_ty.deref(), &self.type_declarations) - .is_none() - { + let bridged_return_type = + BridgedType::new_with_type(return_ty.deref(), &self.type_declarations); + if let Some(ty) = &bridged_return_type { + if ty.as_option().is_some() && attributes.is_swift_initializer { + is_swift_failable_initializer = true; + } + } + if bridged_return_type.is_none() { self.unresolved_types.push(return_ty.deref().clone()); } } - let first_input = func.sig.inputs.iter().next(); let associated_type = self.get_associated_type( @@ -126,6 +131,7 @@ impl<'a> ForeignModParser<'a> { func.clone(), &attributes, &mut local_type_declarations, + is_swift_failable_initializer, )?; if attributes.is_swift_identifiable { @@ -225,10 +231,12 @@ impl<'a> ForeignModParser<'a> { } } } + let func = ParsedExternFn { func, associated_type, is_swift_initializer: attributes.is_swift_initializer, + is_swift_failable_initializer: is_swift_failable_initializer, is_swift_identifiable: attributes.is_swift_identifiable, host_lang, rust_name_override: attributes.rust_name, @@ -294,6 +302,7 @@ impl<'a> ForeignModParser<'a> { func: ForeignItemFn, attributes: &FunctionAttributes, local_type_declarations: &mut HashMap, + is_swift_failable_initializer: bool, ) -> syn::Result> { let associated_type = match first { Some(FnArg::Receiver(recv)) => { @@ -337,6 +346,7 @@ impl<'a> ForeignModParser<'a> { func.clone(), attributes, local_type_declarations, + is_swift_failable_initializer, )?; associated_type } @@ -373,10 +383,19 @@ Otherwise we use a more general error that says that your argument is invalid. ty_string } }; + if is_swift_failable_initializer { + // Safety: since we've already checked ty_string is formatted as "Option<~>" before calling this function. + let last_bracket = ty_string.rfind(">").unwrap(); + + let inner = &ty_string[0..last_bracket]; + let inner = inner.trim_start_matches("Option < ").trim_end_matches(" "); + let ty = self.type_declarations.get(inner); + ty.map(|ty| ty.clone()) + } else { + let ty = self.type_declarations.get(&ty_string); - let ty = self.type_declarations.get(&ty_string); - - ty.map(|ty| ty.clone()) + ty.map(|ty| ty.clone()) + } } else { None }; diff --git a/crates/swift-bridge-ir/src/parse/parse_extern_mod/function_attributes.rs b/crates/swift-bridge-ir/src/parse/parse_extern_mod/function_attributes.rs index 6d40e6d3..16b85b1f 100644 --- a/crates/swift-bridge-ir/src/parse/parse_extern_mod/function_attributes.rs +++ b/crates/swift-bridge-ir/src/parse/parse_extern_mod/function_attributes.rs @@ -277,6 +277,27 @@ mod tests { assert!(func.is_swift_initializer); } + /// Verify that we can parse an failable init function. + #[test] + fn failable_initializer() { + let tokens = quote! { + mod foo { + extern "Rust" { + type Foo; + + #[swift_bridge(init)] + fn bar () -> Option; + } + } + }; + + let module = parse_ok(tokens); + + let func = &module.functions[0]; + assert!(func.is_swift_initializer); + assert!(func.is_swift_failable_initializer); + } + /// Verify that we can parse an init function that takes inputs. #[test] fn initializer_with_inputs() { diff --git a/crates/swift-bridge-ir/src/parsed_extern_fn.rs b/crates/swift-bridge-ir/src/parsed_extern_fn.rs index f01c0d44..2faa67b5 100644 --- a/crates/swift-bridge-ir/src/parsed_extern_fn.rs +++ b/crates/swift-bridge-ir/src/parsed_extern_fn.rs @@ -59,6 +59,10 @@ pub(crate) struct ParsedExternFn { pub host_lang: HostLang, /// Whether or not this function is a Swift initializer. pub is_swift_initializer: bool, + /// Whether or not this function is a Swift failable initializer. + /// For more details, see: + /// [Swift Documentation - Failable Initializers](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/initialization/#Failable-Initializers) + pub is_swift_failable_initializer: bool, /// Whether or not this function should be used for the associated type's Swift /// `Identifiable` protocol implementation. pub is_swift_identifiable: bool, diff --git a/crates/swift-integration-tests/src/option.rs b/crates/swift-integration-tests/src/option.rs index 8a8bd4c4..0fe73015 100644 --- a/crates/swift-integration-tests/src/option.rs +++ b/crates/swift-integration-tests/src/option.rs @@ -148,6 +148,14 @@ mod ffi { fn swift_arg_option_str(arg: Option<&str>) -> bool; // fn swift_reflect_option_str(arg: Option<&str>) -> Option<&str>; } + + extern "Rust" { + #[swift_bridge(Equatable)] + type FailableInitType; + + #[swift_bridge(init)] + fn new(success: bool) -> Option; + } } fn test_rust_calls_swift_option_primitive() { @@ -340,3 +348,16 @@ fn rust_reflect_option_struct_with_no_data( ) -> Option { arg } + +#[derive(PartialEq)] +struct FailableInitType; + +impl FailableInitType { + fn new(success: bool) -> Option { + if success { + Some(FailableInitType) + } else { + None + } + } +} From 889b4cdec8ae43fedc18b5574a5169245727208c Mon Sep 17 00:00:00 2001 From: NiwakaDev Date: Thu, 8 Aug 2024 21:55:14 +0900 Subject: [PATCH 2/4] add document and more test --- .../SwiftRustIntegrationTestRunner.xcodeproj/project.pbxproj | 2 ++ .../SwiftRustIntegrationTestRunnerTests/OptionTests.swift | 4 +++- crates/swift-integration-tests/src/option.rs | 5 +++++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/SwiftRustIntegrationTestRunner/SwiftRustIntegrationTestRunner.xcodeproj/project.pbxproj b/SwiftRustIntegrationTestRunner/SwiftRustIntegrationTestRunner.xcodeproj/project.pbxproj index 07afdcc4..607934a0 100644 --- a/SwiftRustIntegrationTestRunner/SwiftRustIntegrationTestRunner.xcodeproj/project.pbxproj +++ b/SwiftRustIntegrationTestRunner/SwiftRustIntegrationTestRunner.xcodeproj/project.pbxproj @@ -582,6 +582,7 @@ COMBINE_HIDPI_IMAGES = YES; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"SwiftRustIntegrationTestRunner/Preview Content\""; + DEVELOPMENT_TEAM = ""; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_KEY_NSHumanReadableCopyright = ""; @@ -612,6 +613,7 @@ COMBINE_HIDPI_IMAGES = YES; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"SwiftRustIntegrationTestRunner/Preview Content\""; + DEVELOPMENT_TEAM = ""; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_KEY_NSHumanReadableCopyright = ""; diff --git a/SwiftRustIntegrationTestRunner/SwiftRustIntegrationTestRunnerTests/OptionTests.swift b/SwiftRustIntegrationTestRunner/SwiftRustIntegrationTestRunnerTests/OptionTests.swift index ba3c5df8..34cf859a 100644 --- a/SwiftRustIntegrationTestRunner/SwiftRustIntegrationTestRunnerTests/OptionTests.swift +++ b/SwiftRustIntegrationTestRunner/SwiftRustIntegrationTestRunnerTests/OptionTests.swift @@ -221,8 +221,10 @@ class OptionTests: XCTestCase { XCTAssertNil(reflectedNone) } + /// Verify that we can use failable initializers defined on the Rust side. func testFailableInitializer() { XCTAssertEqual(FailableInitType(false), nil) - XCTAssertNotEqual(FailableInitType(true), nil) + let failableInitType = FailableInitType(true) + XCTAssertEqual(failableInitType!.count(), 132) } } diff --git a/crates/swift-integration-tests/src/option.rs b/crates/swift-integration-tests/src/option.rs index 0fe73015..8ad058b5 100644 --- a/crates/swift-integration-tests/src/option.rs +++ b/crates/swift-integration-tests/src/option.rs @@ -155,6 +155,7 @@ mod ffi { #[swift_bridge(init)] fn new(success: bool) -> Option; + fn count(&self) -> i32; } } @@ -360,4 +361,8 @@ impl FailableInitType { None } } + + fn count(&self) -> i32 { + 132 + } } From 55ab89cf307ef40e837950ca797f061e79fa3cee Mon Sep 17 00:00:00 2001 From: NiwakaDev Date: Fri, 9 Aug 2024 00:20:26 +0900 Subject: [PATCH 3/4] move test to codegen_test --- .../opaque_rust_type_codegen_tests.rs | 71 +++++++++++++++++++ .../src/codegen/generate_swift.rs | 40 ----------- 2 files changed, 71 insertions(+), 40 deletions(-) diff --git a/crates/swift-bridge-ir/src/codegen/codegen_tests/opaque_rust_type_codegen_tests.rs b/crates/swift-bridge-ir/src/codegen/codegen_tests/opaque_rust_type_codegen_tests.rs index fcb4420c..2d2a6f03 100644 --- a/crates/swift-bridge-ir/src/codegen/codegen_tests/opaque_rust_type_codegen_tests.rs +++ b/crates/swift-bridge-ir/src/codegen/codegen_tests/opaque_rust_type_codegen_tests.rs @@ -456,3 +456,74 @@ typedef struct MyType MyType; .test(); } } + +/// Verify that we generated a Swift class with a failable init method. +mod extern_rust_class_with_failable_init { + use super::*; + + fn bridge_module_tokens() -> TokenStream { + quote! { + mod foo { + extern "Rust" { + type Foo; + + #[swift_bridge(init)] + fn new() -> Option; + } + } + } + } + + fn expected_rust_tokens() -> ExpectedRustTokens { + ExpectedRustTokens::Contains(quote! { + # [export_name = "__swift_bridge__$Foo$new"] + pub extern "C" fn __swift_bridge__Foo_new () -> * mut super :: Foo { + if let Some (val) = super :: Foo :: new () { + Box :: into_raw (Box :: new (val)) + } else { + std :: ptr :: null_mut () + } + } + }) + } + + const EXPECTED_SWIFT: ExpectedSwiftCode = ExpectedSwiftCode::ContainsAfterTrim( + r#" +public class Foo: FooRefMut { + var isOwned: Bool = true + + public override init(ptr: UnsafeMutableRawPointer) { + super.init(ptr: ptr) + } + + deinit { + if isOwned { + __swift_bridge__$Foo$_free(ptr) + } + } +} +extension Foo { + public convenience init?() { + guard let val = __swift_bridge__$Foo$new() else { return nil }; self.init(ptr: val) + } +} +"#, + ); + + const EXPECTED_C_HEADER: ExpectedCHeader = ExpectedCHeader::ContainsAfterTrim( + r#" +void* __swift_bridge__$Foo$new(void); +"#, + ); + + #[test] + fn extern_rust_class_with_failable_init() { + CodegenTest { + bridge_module: bridge_module_tokens().into(), + expected_rust_tokens: expected_rust_tokens(), + expected_swift_code: EXPECTED_SWIFT, + expected_c_header: EXPECTED_C_HEADER, + } + .test(); + } +} diff --git a/crates/swift-bridge-ir/src/codegen/generate_swift.rs b/crates/swift-bridge-ir/src/codegen/generate_swift.rs index c8fbbe39..491cd818 100644 --- a/crates/swift-bridge-ir/src/codegen/generate_swift.rs +++ b/crates/swift-bridge-ir/src/codegen/generate_swift.rs @@ -606,46 +606,6 @@ func __swift_bridge__Foo_new (_ a: UInt8) -> UnsafeMutableRawPointer { assert_trimmed_generated_contains_trimmed_expected(&generated, &expected); } - /// Verify that we generated a Swift class with a failable init method. - #[test] - fn class_with_failable_init() { - let tokens = quote! { - mod foo { - extern "Rust" { - type Foo; - - #[swift_bridge(init)] - fn new() -> Option; - } - } - }; - let module: SwiftBridgeModule = parse_quote!(#tokens); - let generated = module.generate_swift(&CodegenConfig::no_features_enabled()); - - let expected = r#" -public class Foo: FooRefMut { - var isOwned: Bool = true - - public override init(ptr: UnsafeMutableRawPointer) { - super.init(ptr: ptr) - } - - deinit { - if isOwned { - __swift_bridge__$Foo$_free(ptr) - } - } -} -extension Foo { - public convenience init?() { - guard let val = __swift_bridge__$Foo$new() else { return nil }; self.init(ptr: val) - } -} -"#; - - assert_trimmed_generated_contains_trimmed_expected(&generated, expected); - } - /// Verify that we generated a Swift class with an init method. #[test] fn class_with_init() { From e2a531e2deabfee55c88eefab812cfc5516afca1 Mon Sep 17 00:00:00 2001 From: NiwakaDev Date: Fri, 9 Aug 2024 07:47:10 +0900 Subject: [PATCH 4/4] remove DEVELOPMENT_TEAM --- .../SwiftRustIntegrationTestRunner.xcodeproj/project.pbxproj | 2 -- 1 file changed, 2 deletions(-) diff --git a/SwiftRustIntegrationTestRunner/SwiftRustIntegrationTestRunner.xcodeproj/project.pbxproj b/SwiftRustIntegrationTestRunner/SwiftRustIntegrationTestRunner.xcodeproj/project.pbxproj index 607934a0..07afdcc4 100644 --- a/SwiftRustIntegrationTestRunner/SwiftRustIntegrationTestRunner.xcodeproj/project.pbxproj +++ b/SwiftRustIntegrationTestRunner/SwiftRustIntegrationTestRunner.xcodeproj/project.pbxproj @@ -582,7 +582,6 @@ COMBINE_HIDPI_IMAGES = YES; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"SwiftRustIntegrationTestRunner/Preview Content\""; - DEVELOPMENT_TEAM = ""; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_KEY_NSHumanReadableCopyright = ""; @@ -613,7 +612,6 @@ COMBINE_HIDPI_IMAGES = YES; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"SwiftRustIntegrationTestRunner/Preview Content\""; - DEVELOPMENT_TEAM = ""; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_KEY_NSHumanReadableCopyright = "";