diff --git a/Apps/HappyHour/README.md b/Apps/HappyHour/README.md index d7ff852..12cb941 100644 --- a/Apps/HappyHour/README.md +++ b/Apps/HappyHour/README.md @@ -1,2 +1,2 @@ # HappyHour -Tell HappyHour what ingredients you have, give your phone a shake (or hit the mix button on desktop) and make some drinks! \ No newline at end of file +A complete bartending companion app. Tap the ingredients you have in your kitchen, give your phone a shake, and make some drinks! diff --git a/README.md b/README.md index dc08fe5..dbff92f 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ The Windows Bridge for iOS (also referred to as WinObjC) is a Microsoft open-sou The WinObjC sample apps can be found in */Apps*. They include: - [**HappyHour**](/Apps/HappyHour) - A complete bartending companion app. Tap the ingredients you have in your kitchen, give your phone a shake and make some drinks! + A complete bartending companion app. Tap the ingredients you have in your kitchen, give your phone a shake, and make some drinks! - [**ToDoList**](/Apps/ToDoList) @@ -34,30 +34,30 @@ Sample code and projects for common scenarios can be found under */Scenarios*. T This example demonstrates popping local toast notifications on Windows 10 using Objective-C projections. -- [Using Projections: **Cortana**](/Scenarios/Cortana) - *Coming soon* +- [Using Projections: **In-App Purchases**](/Scenarios/In-App Purchases) - Cortana is your personal assistant on Windows 10. This example shows how to integrate Cortana voice commands into your app using Objective-C projections. + This example shows how to replace StoreKit in your app and add support for in-app purchases on Windows 10 using Objective-C projections. -- [Using Projections: **In-App Purchases**](/Scenarios/In-App Purchases) - *Coming soon* +- Using Projections: **Cortana** - *Coming soon* - This example shows how to replace StoreKit in your app and add support for in-app purchases on Windows 10 using Objective-C projections. + Cortana is your personal assistant on Windows 10. This example shows how to integrate Cortana voice commands into your app using Objective-C projections. -- [Using Projections: **Push Notifications**](/Scenarios/Push Notifications) - *Coming soon* +- Using Projections: **Push Notifications** - *Coming soon* This example demonstrates how to replace Apple Push Notification Service with Windows Notification Service using Objective-C so you can send push notifications to Windows 10 devices. -- [Using Projections: **Maps**](/Scenarios/Maps) - *Coming soon* +- Using Projections: **Maps** - *Coming soon* This example demonstrates how to replace MapKit or Google Maps-based maps in your Objective-C code base with XAML controls and Bing Maps. -- [Using Projections: **Ads**](/Scenarios/Ads) - *Coming soon* +- Using Projections: **Ads** - *Coming soon* This example will demonstrate the addition of Microsoft ads to your Objective-C code base. -- [Using Projections: **Game controllers**](/Scenarios/GameControllers) - *Coming soon* +- Using Projections: **Game controllers** - *Coming soon* This sample will introduce you to handle key and controller input in Windows using Objective-C. - -- [Using Projections: **Games for Windows 10**](/Scenarios/GameKit) - *Coming soon* + +- Using Projections: **Games for Windows 10** - *Coming soon* This sample will instruct you in the addition of leaderboards and other Windows game components using Objective-C. diff --git a/Scenarios/In-App Purchases/IAP-sample/IAP-sample.xcodeproj/project.pbxproj b/Scenarios/In-App Purchases/IAP-sample/IAP-sample.xcodeproj/project.pbxproj new file mode 100644 index 0000000..a89b2e5 --- /dev/null +++ b/Scenarios/In-App Purchases/IAP-sample/IAP-sample.xcodeproj/project.pbxproj @@ -0,0 +1,307 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + F4DFFB701CD95B85003A2FF7 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = F4DFFB6F1CD95B85003A2FF7 /* main.m */; }; + F4DFFB731CD95B85003A2FF7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = F4DFFB721CD95B85003A2FF7 /* AppDelegate.m */; }; + F4DFFB761CD95B85003A2FF7 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = F4DFFB751CD95B85003A2FF7 /* ViewController.m */; }; + F4DFFB791CD95B85003A2FF7 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F4DFFB771CD95B85003A2FF7 /* Main.storyboard */; }; + F4DFFB7B1CD95B85003A2FF7 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F4DFFB7A1CD95B85003A2FF7 /* Assets.xcassets */; }; + F4DFFB7E1CD95B85003A2FF7 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F4DFFB7C1CD95B85003A2FF7 /* LaunchScreen.storyboard */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + F4DFFB6B1CD95B85003A2FF7 /* IAP-sample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "IAP-sample.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + F4DFFB6F1CD95B85003A2FF7 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + F4DFFB711CD95B85003A2FF7 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; + F4DFFB721CD95B85003A2FF7 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + F4DFFB741CD95B85003A2FF7 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; + F4DFFB751CD95B85003A2FF7 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; + F4DFFB781CD95B85003A2FF7 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + F4DFFB7A1CD95B85003A2FF7 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + F4DFFB7D1CD95B85003A2FF7 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + F4DFFB7F1CD95B85003A2FF7 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + F4DFFB681CD95B85003A2FF7 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + F4DFFB621CD95B85003A2FF7 = { + isa = PBXGroup; + children = ( + F4DFFB6D1CD95B85003A2FF7 /* IAP-sample */, + F4DFFB6C1CD95B85003A2FF7 /* Products */, + ); + sourceTree = ""; + }; + F4DFFB6C1CD95B85003A2FF7 /* Products */ = { + isa = PBXGroup; + children = ( + F4DFFB6B1CD95B85003A2FF7 /* IAP-sample.app */, + ); + name = Products; + sourceTree = ""; + }; + F4DFFB6D1CD95B85003A2FF7 /* IAP-sample */ = { + isa = PBXGroup; + children = ( + F4DFFB711CD95B85003A2FF7 /* AppDelegate.h */, + F4DFFB721CD95B85003A2FF7 /* AppDelegate.m */, + F4DFFB741CD95B85003A2FF7 /* ViewController.h */, + F4DFFB751CD95B85003A2FF7 /* ViewController.m */, + F4DFFB771CD95B85003A2FF7 /* Main.storyboard */, + F4DFFB7A1CD95B85003A2FF7 /* Assets.xcassets */, + F4DFFB7C1CD95B85003A2FF7 /* LaunchScreen.storyboard */, + F4DFFB7F1CD95B85003A2FF7 /* Info.plist */, + F4DFFB6E1CD95B85003A2FF7 /* Supporting Files */, + ); + path = "IAP-sample"; + sourceTree = ""; + }; + F4DFFB6E1CD95B85003A2FF7 /* Supporting Files */ = { + isa = PBXGroup; + children = ( + F4DFFB6F1CD95B85003A2FF7 /* main.m */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + F4DFFB6A1CD95B85003A2FF7 /* IAP-sample */ = { + isa = PBXNativeTarget; + buildConfigurationList = F4DFFB821CD95B85003A2FF7 /* Build configuration list for PBXNativeTarget "IAP-sample" */; + buildPhases = ( + F4DFFB671CD95B85003A2FF7 /* Sources */, + F4DFFB681CD95B85003A2FF7 /* Frameworks */, + F4DFFB691CD95B85003A2FF7 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "IAP-sample"; + productName = "IAP-sample"; + productReference = F4DFFB6B1CD95B85003A2FF7 /* IAP-sample.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + F4DFFB631CD95B85003A2FF7 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0700; + ORGANIZATIONNAME = Islandwood; + TargetAttributes = { + F4DFFB6A1CD95B85003A2FF7 = { + CreatedOnToolsVersion = 7.0.1; + }; + }; + }; + buildConfigurationList = F4DFFB661CD95B85003A2FF7 /* Build configuration list for PBXProject "IAP-sample" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = F4DFFB621CD95B85003A2FF7; + productRefGroup = F4DFFB6C1CD95B85003A2FF7 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + F4DFFB6A1CD95B85003A2FF7 /* IAP-sample */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + F4DFFB691CD95B85003A2FF7 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F4DFFB7E1CD95B85003A2FF7 /* LaunchScreen.storyboard in Resources */, + F4DFFB7B1CD95B85003A2FF7 /* Assets.xcassets in Resources */, + F4DFFB791CD95B85003A2FF7 /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + F4DFFB671CD95B85003A2FF7 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F4DFFB761CD95B85003A2FF7 /* ViewController.m in Sources */, + F4DFFB731CD95B85003A2FF7 /* AppDelegate.m in Sources */, + F4DFFB701CD95B85003A2FF7 /* main.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + F4DFFB771CD95B85003A2FF7 /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + F4DFFB781CD95B85003A2FF7 /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + F4DFFB7C1CD95B85003A2FF7 /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + F4DFFB7D1CD95B85003A2FF7 /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + F4DFFB801CD95B85003A2FF7 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + F4DFFB811CD95B85003A2FF7 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + F4DFFB831CD95B85003A2FF7 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + INFOPLIST_FILE = "IAP-sample/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "Microsoft.IAP-sample"; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + F4DFFB841CD95B85003A2FF7 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + INFOPLIST_FILE = "IAP-sample/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "Microsoft.IAP-sample"; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + F4DFFB661CD95B85003A2FF7 /* Build configuration list for PBXProject "IAP-sample" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F4DFFB801CD95B85003A2FF7 /* Debug */, + F4DFFB811CD95B85003A2FF7 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F4DFFB821CD95B85003A2FF7 /* Build configuration list for PBXNativeTarget "IAP-sample" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F4DFFB831CD95B85003A2FF7 /* Debug */, + F4DFFB841CD95B85003A2FF7 /* Release */, + ); + defaultConfigurationIsVisible = 0; + }; +/* End XCConfigurationList section */ + }; + rootObject = F4DFFB631CD95B85003A2FF7 /* Project object */; +} diff --git a/Scenarios/In-App Purchases/IAP-sample/IAP-sample.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Scenarios/In-App Purchases/IAP-sample/IAP-sample.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..22855f6 --- /dev/null +++ b/Scenarios/In-App Purchases/IAP-sample/IAP-sample.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/Scenarios/In-App Purchases/IAP-sample/IAP-sample/AppDelegate.h b/Scenarios/In-App Purchases/IAP-sample/IAP-sample/AppDelegate.h new file mode 100644 index 0000000..b6b6682 --- /dev/null +++ b/Scenarios/In-App Purchases/IAP-sample/IAP-sample/AppDelegate.h @@ -0,0 +1,29 @@ +//****************************************************************************** +// +// Copyright (c) 2016 Microsoft Corporation. All rights reserved. +// +// This code is licensed under the MIT License (MIT). +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +//****************************************************************************** + +///////////////////////////////////////// +// WinObjC Sample - In-App Purchases // +// github.com/Microsoft/WinObjC-samples// +///////////////////////////////////////// + +#import + +@interface AppDelegate : UIResponder + +@property (strong, nonatomic) UIWindow *window; + +@end + diff --git a/Scenarios/In-App Purchases/IAP-sample/IAP-sample/AppDelegate.m b/Scenarios/In-App Purchases/IAP-sample/IAP-sample/AppDelegate.m new file mode 100644 index 0000000..3b4ad8e --- /dev/null +++ b/Scenarios/In-App Purchases/IAP-sample/IAP-sample/AppDelegate.m @@ -0,0 +1,74 @@ +//****************************************************************************** +// +// Copyright (c) 2016 Microsoft Corporation. All rights reserved. +// +// This code is licensed under the MIT License (MIT). +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +//****************************************************************************** + +///////////////////////////////////////// +// WinObjC Sample - In-App Purchases // +// github.com/Microsoft/WinObjC-samples// +///////////////////////////////////////// + +#import "AppDelegate.h" + +@interface AppDelegate () + +@end + +#ifdef WINOBJC +// Tell the WinObjC runtime the application size +@implementation UIApplication (UIApplicationInitialStartupMode) ++ (void)setStartupDisplayMode:(WOCDisplayMode*)mode { + mode.autoMagnification = TRUE; + mode.sizeUIWindowToFit = TRUE; + mode.clampScaleToClosestExpected = FALSE; + mode.fixedWidth = 0; + mode.fixedHeight = 0; + mode.magnification = 1.0; +} +@end +#endif + +@implementation AppDelegate + +- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions +{ + return YES; +} + +- (void)applicationWillResignActive:(UIApplication *)application +{ + +} + +- (void)applicationDidEnterBackground:(UIApplication *)application +{ + +} + +- (void)applicationWillEnterForeground:(UIApplication *)application +{ + +} + +- (void)applicationDidBecomeActive:(UIApplication *)application +{ + +} + +- (void)applicationWillTerminate:(UIApplication *)application +{ + +} + +@end diff --git a/Scenarios/In-App Purchases/IAP-sample/IAP-sample/Assets.xcassets/AppIcon.appiconset/Contents.json b/Scenarios/In-App Purchases/IAP-sample/IAP-sample/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..36d2c80 --- /dev/null +++ b/Scenarios/In-App Purchases/IAP-sample/IAP-sample/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,68 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "3x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Scenarios/In-App Purchases/IAP-sample/IAP-sample/Base.lproj/LaunchScreen.storyboard b/Scenarios/In-App Purchases/IAP-sample/IAP-sample/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..a84d201 --- /dev/null +++ b/Scenarios/In-App Purchases/IAP-sample/IAP-sample/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Scenarios/In-App Purchases/IAP-sample/IAP-sample/Base.lproj/Main.storyboard b/Scenarios/In-App Purchases/IAP-sample/IAP-sample/Base.lproj/Main.storyboard new file mode 100644 index 0000000..e6eb5ab --- /dev/null +++ b/Scenarios/In-App Purchases/IAP-sample/IAP-sample/Base.lproj/Main.storyboard @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Scenarios/In-App Purchases/IAP-sample/IAP-sample/Info.plist b/Scenarios/In-App Purchases/IAP-sample/IAP-sample/Info.plist new file mode 100644 index 0000000..40c6215 --- /dev/null +++ b/Scenarios/In-App Purchases/IAP-sample/IAP-sample/Info.plist @@ -0,0 +1,47 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/Scenarios/In-App Purchases/IAP-sample/IAP-sample/ViewController.h b/Scenarios/In-App Purchases/IAP-sample/IAP-sample/ViewController.h new file mode 100644 index 0000000..da79ff2 --- /dev/null +++ b/Scenarios/In-App Purchases/IAP-sample/IAP-sample/ViewController.h @@ -0,0 +1,28 @@ +//****************************************************************************** +// +// Copyright (c) 2016 Microsoft Corporation. All rights reserved. +// +// This code is licensed under the MIT License (MIT). +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +//****************************************************************************** + +///////////////////////////////////////// +// WinObjC Sample - In-App Purchases // +// github.com/Microsoft/WinObjC-samples// +///////////////////////////////////////// + + +#import + +@interface ViewController : UIViewController + +@end + diff --git a/Scenarios/In-App Purchases/IAP-sample/IAP-sample/ViewController.m b/Scenarios/In-App Purchases/IAP-sample/IAP-sample/ViewController.m new file mode 100644 index 0000000..b2d984e --- /dev/null +++ b/Scenarios/In-App Purchases/IAP-sample/IAP-sample/ViewController.m @@ -0,0 +1,239 @@ +//****************************************************************************** +// +// Copyright (c) 2016 Microsoft Corporation. All rights reserved. +// +// This code is licensed under the MIT License (MIT). +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +//****************************************************************************** + +///////////////////////////////////////// +// WinObjC Sample - In-App Purchases // +// github.com/Microsoft/WinObjC-samples// +///////////////////////////////////////// + +#import "ViewController.h" + +#ifdef WINOBJC +#import +#endif + +@interface ViewController () + +@property UILabel *demoTitle; +@property UILabel *demoInfo; +@property UILabel *label; +@property UIButton *button; + +@end + +@implementation ViewController + +// WinObjC example app constants +#ifdef WINOBJC +static NSString * const removableFont = @"Comic Sans MS"; +#else +static NSString * const removableFont = @"Chalkduster"; +#endif + +static NSString * const kTitleString = @"In-App Purchases"; +static NSString * const kButtonText = @"Change font to System font"; +static NSString * const kLabelInitialText = @"Simulated - no purchase will be made."; + +BOOL systemFont = NO; + +#ifdef WINOBJC +WSProductListing* product1; +#endif + + +- (void)formatFonts { + // Update fonts + if (systemFont) { + self.label.font = [UIFont systemFontOfSize:[UIFont smallSystemFontSize]]; + self.demoInfo.font = [UIFont boldSystemFontOfSize:[UIFont labelFontSize]]; + self.demoTitle.font = [UIFont boldSystemFontOfSize:[UIFont labelFontSize]]; + self.button.titleLabel.font = [UIFont systemFontOfSize:[UIFont smallSystemFontSize]]; + } + else { + self.label.font = [UIFont fontWithName:removableFont size:10]; + self.demoInfo.font = [UIFont fontWithName:removableFont size:14]; + self.demoTitle.font = [UIFont boldSystemFontOfSize:[UIFont labelFontSize]]; + self.button.titleLabel.font = [UIFont fontWithName:removableFont size:12]; + } + +} + +- (void)viewDidLoad +{ + [super viewDidLoad]; + + // Set up the demo title label + self.demoTitle = [UILabel new]; + self.demoTitle.text = [NSString stringWithFormat:@"WinObjC: %@", kTitleString]; + self.demoTitle.font = [UIFont boldSystemFontOfSize:[UIFont labelFontSize]]; + self.demoTitle.textAlignment = NSTextAlignmentCenter; + self.demoTitle.translatesAutoresizingMaskIntoConstraints = NO; + + // Set up the demo info label + self.demoInfo = [UILabel new]; + NSString * infoString = [NSString stringWithFormat:@"This WinObjC sample project demonstrates how to enable In-App Purchases for Windows 10 using Objective-C. The button below simulates the purchase of a non-consumable product that changes the UI to a font other than %@.", removableFont]; + + self.demoInfo.text = infoString; + self.demoInfo.font = [UIFont fontWithName:removableFont size:14]; + self.demoInfo.numberOfLines = 0; + self.demoInfo.lineBreakMode = NSLineBreakByWordWrapping; + self.demoInfo.textAlignment = NSTextAlignmentCenter; + self.demoInfo.translatesAutoresizingMaskIntoConstraints = NO; + + // Set up the button + self.button = [UIButton buttonWithType:UIButtonTypeRoundedRect]; + self.button.titleLabel.font = [UIFont fontWithName:removableFont size:12]; + [self.button setTitle:kButtonText + forState:UIControlStateNormal]; + [self.button setTranslatesAutoresizingMaskIntoConstraints:NO]; + [self.button addTarget:self + action:@selector(storeButtonPressed) + forControlEvents:UIControlEventTouchDown]; + + // Set up the label + self.label = [UILabel new]; + self.label.text = kLabelInitialText; + self.label.font = [UIFont fontWithName:removableFont size:10]; + self.label.numberOfLines = 0; + self.label.lineBreakMode = NSLineBreakByWordWrapping; + self.label.textAlignment = NSTextAlignmentCenter; + self.label.translatesAutoresizingMaskIntoConstraints = NO; + + [self.view addSubview:self.demoTitle]; + [self.view addSubview:self.demoInfo]; + [self.view addSubview:self.button]; + [self.view addSubview:self.label]; + + // Layout constraints + NSDictionary *metrics = @{ @"pad": @80.0, @"margin": @40, @"demoInfoHeight": @120 }; + NSDictionary *views = @{ @"title" : self.demoTitle, + @"info" : self.demoInfo, + @"button" : self.button, + @"label" : self.label + }; + + [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[title]-|" + options:0 + metrics:metrics + views:views]]; + [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[info]-|" + options:0 + metrics:metrics + views:views]]; + [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[button]-|" + options:0 + metrics:metrics + views:views]]; + [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[label]-|" + options:0 + metrics:metrics + views:views]]; + [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-pad-[title]-[info(demoInfoHeight)]-margin-[button]-[label]" + options:0 + metrics:metrics + views:views]]; + + [self formatFonts]; + +#ifdef WINOBJC + + // Mock store + WFUri* mockStoreURI = [WFUri makeUri:@"ms-appx:///in-app-purchase.xml"]; + + // Open XML containing the product information in the store [mock or otherwise]. + [WSStorageFile getFileFromApplicationUriAsync:mockStoreURI + success:^void(WSStorageFile* result) { + + [WSCurrentAppSimulator reloadSimulatorAsync:result].completed = + ^void(RTObject* asyncInfo,WFAsyncStatus asyncStatus) { + + [WSCurrentAppSimulator loadListingInformationAsyncWithSuccess:^void(WSListingInformation* info) { + + product1 = [info.productListings objectForKey:@"product0"]; + NSString* button1Title = [NSString stringWithFormat:@"Buy %@ (%@) now", product1.name, product1.formattedPrice]; + [self.button setTitle:button1Title + forState:UIControlStateNormal]; + [self.button addTarget:self + action:@selector(storeButtonPressed) + forControlEvents:UIControlEventTouchDown]; + } + failure:^(NSError* err) { + NSLog(@"Hit error %@ when attempting to load listing information!", err); + }]; + }; + } + + failure:^(NSError* err) { + NSLog(@"Hit error %@ when attempting to load mock store XML data!", err); + } + ]; + +#endif + +#ifdef WINOBJC + [[UIApplication sharedApplication] setStatusBarHidden:YES]; // Deprecated in iOS +#endif +} + +- (BOOL) prefersStatusBarHidden +{ + return YES; +} + +- (void)storeButtonPressed +{ + NSLog(@"Pushing store button."); +#ifdef WINOBJC + [self buyProduct:product1.productId name:product1.name]; +#endif + [self formatFonts]; +} + +#ifdef WINOBJC +-(void)buyProduct:(NSString*)productId name:(NSString*)name { + WSLicenseInformation* licenseInformation = [WSCurrentAppSimulator licenseInformation]; + NSDictionary* productLicenses = [licenseInformation productLicenses]; + WSProductLicense* license = productLicenses[productId]; + + if (!license.isActive) { + self.label.text = [NSString stringWithFormat:@"Buying %@", name]; + [WSCurrentAppSimulator requestProductPurchaseAsync:productId + includeReceipt:TRUE + success:^void(NSString* result) { + // validate the purchase + NSDictionary* productLicenses = [[WSCurrentAppSimulator licenseInformation] productLicenses]; + WSProductLicense* license = productLicenses[productId]; + if (license.isActive) { + systemFont = YES; + self.label.text = [NSString stringWithFormat:@"You bought %@", name]; + NSLog(@"You purchased %@.", name); + [self formatFonts]; + }; + } + failure:^(NSError* err) { + self.label.text = [NSString stringWithFormat:@"You did not buy %@", name]; + NSLog(@"You did not purchase %@.\nError:%@", name, err); + [self formatFonts]; + }]; + } else { + self.label.text = [NSString stringWithFormat:@"You already bought %@", name]; + NSLog(@"%@ already purchased.", name); + [self formatFonts]; +} +} +#endif + +@end diff --git a/Scenarios/In-App Purchases/IAP-sample/IAP-sample/in-app-purchase.xml b/Scenarios/In-App Purchases/IAP-sample/IAP-sample/in-app-purchase.xml new file mode 100644 index 0000000..9dddacc Binary files /dev/null and b/Scenarios/In-App Purchases/IAP-sample/IAP-sample/in-app-purchase.xml differ diff --git a/Scenarios/In-App Purchases/IAP-sample/IAP-sample/main.m b/Scenarios/In-App Purchases/IAP-sample/IAP-sample/main.m new file mode 100644 index 0000000..8ca17a2 --- /dev/null +++ b/Scenarios/In-App Purchases/IAP-sample/IAP-sample/main.m @@ -0,0 +1,29 @@ +//****************************************************************************** +// +// Copyright (c) 2016 Microsoft Corporation. All rights reserved. +// +// This code is licensed under the MIT License (MIT). +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +//****************************************************************************** + +///////////////////////////////////////// +// WinObjC Sample - In-App Purchases // +// github.com/Microsoft/WinObjC-samples// +///////////////////////////////////////// + +#import +#import "AppDelegate.h" + +int main(int argc, char * argv[]) { + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); + } +} diff --git a/Scenarios/In-App Purchases/README.md b/Scenarios/In-App Purchases/README.md index 1a5d43d..428ef86 100644 --- a/Scenarios/In-App Purchases/README.md +++ b/Scenarios/In-App Purchases/README.md @@ -1,3 +1,7 @@ # Using Projections: In-App Purchases -*Coming soon.* +*Work in progress.* + +This xcodeproj can be processed by VSImporter and run on a Windows 10 machine using VS 2015 Update 2. + +The calls to the mock store are being performed with less granularity than I would like, but they are valid patterns that can be substituted with real Windows Store IAP.