From c82ae851bcc5072356ab2308e41855d5c1ed8623 Mon Sep 17 00:00:00 2001 From: Andy Dent Date: Thu, 9 Jun 2022 04:46:48 +0800 Subject: [PATCH 1/7] Adding Messenger extension Target imUrlDataAppSUI IM project target added as iMessage extension --- imUrlDataApp/README.md | 2 +- .../imUrlDataAppSUI Code Change Diary.txt | 8 + .../Assets.xcassets/Contents.json | 6 + .../Contents.json | 78 +++++++ .../Base.lproj/MainInterface.storyboard | 37 ++++ imUrlDataAppSUI/imUrlDataAppSUI IM/Info.plist | 13 ++ .../MessagesViewController.swift | 66 ++++++ .../imUrlDataAppSUI.xcodeproj/project.pbxproj | 191 +++++++++++++++++- 8 files changed, 399 insertions(+), 2 deletions(-) create mode 100644 imUrlDataAppSUI/imUrlDataAppSUI IM/Assets.xcassets/Contents.json create mode 100644 imUrlDataAppSUI/imUrlDataAppSUI IM/Assets.xcassets/iMessage App Icon.stickersiconset/Contents.json create mode 100644 imUrlDataAppSUI/imUrlDataAppSUI IM/Base.lproj/MainInterface.storyboard create mode 100644 imUrlDataAppSUI/imUrlDataAppSUI IM/Info.plist create mode 100644 imUrlDataAppSUI/imUrlDataAppSUI IM/MessagesViewController.swift diff --git a/imUrlDataApp/README.md b/imUrlDataApp/README.md index 9722ce5..1c27579 100644 --- a/imUrlDataApp/README.md +++ b/imUrlDataApp/README.md @@ -25,7 +25,7 @@ Note another strong reason to have the companion app installable is to support I Apple provide [MFMessageComposeViewController](https://developer.apple.com/documentation/messageui/mfmessagecomposeviewcontroller) so you can compose messages inside an app. This is documented as being mostly about sending text **but** you can also assign the `message` property which is an `MSMessage`. The documentation misleadingly says _To display your iMessage app, create and assign an MSMessage object to this property._ but there's no such automatic behaviour. -What _does_ seem to happen, magically behind the scene, is that the iMessage delivered is typed for the iMessage Extension App bundled with your sending app. ie: you can only use this technique when there's an extension. +What _does_ seem to happen, magically behind the scene, is that the iMessage delivered is typed for the iMessage Extension App bundled with your sending app. ie: **you can only use this technique when there's an extension.** ## Project Structure diff --git a/imUrlDataAppSUI/imUrlDataAppSUI Code Change Diary.txt b/imUrlDataAppSUI/imUrlDataAppSUI Code Change Diary.txt index 6601976..1e687d3 100644 --- a/imUrlDataAppSUI/imUrlDataAppSUI Code Change Diary.txt +++ b/imUrlDataAppSUI/imUrlDataAppSUI Code Change Diary.txt @@ -12,3 +12,11 @@ imUrlDataAppSUI - project created as Single View App ContentView.swift - filled in blank View with four buttons and switches plus static label, in stack views - added state variables with toggles so icon opacity is dependent + +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +Adding Messenger extension Target +2022-06-08 + +imUrlDataAppSUI IM project target added as iMessage extension +(comes in with Storyboard) + diff --git a/imUrlDataAppSUI/imUrlDataAppSUI IM/Assets.xcassets/Contents.json b/imUrlDataAppSUI/imUrlDataAppSUI IM/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/imUrlDataAppSUI/imUrlDataAppSUI IM/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/imUrlDataAppSUI/imUrlDataAppSUI IM/Assets.xcassets/iMessage App Icon.stickersiconset/Contents.json b/imUrlDataAppSUI/imUrlDataAppSUI IM/Assets.xcassets/iMessage App Icon.stickersiconset/Contents.json new file mode 100644 index 0000000..bc3c4ee --- /dev/null +++ b/imUrlDataAppSUI/imUrlDataAppSUI IM/Assets.xcassets/iMessage App Icon.stickersiconset/Contents.json @@ -0,0 +1,78 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "60x45" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "60x45" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "67x50" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "74x55" + }, + { + "idiom" : "ios-marketing", + "scale" : "1x", + "size" : "1024x1024" + }, + { + "idiom" : "universal", + "platform" : "ios", + "scale" : "2x", + "size" : "27x20" + }, + { + "idiom" : "universal", + "platform" : "ios", + "scale" : "3x", + "size" : "27x20" + }, + { + "idiom" : "universal", + "platform" : "ios", + "scale" : "2x", + "size" : "32x24" + }, + { + "idiom" : "universal", + "platform" : "ios", + "scale" : "3x", + "size" : "32x24" + }, + { + "idiom" : "ios-marketing", + "platform" : "ios", + "scale" : "1x", + "size" : "1024x768" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/imUrlDataAppSUI/imUrlDataAppSUI IM/Base.lproj/MainInterface.storyboard b/imUrlDataAppSUI/imUrlDataAppSUI IM/Base.lproj/MainInterface.storyboard new file mode 100644 index 0000000..36e2d49 --- /dev/null +++ b/imUrlDataAppSUI/imUrlDataAppSUI IM/Base.lproj/MainInterface.storyboard @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/imUrlDataAppSUI/imUrlDataAppSUI IM/Info.plist b/imUrlDataAppSUI/imUrlDataAppSUI IM/Info.plist new file mode 100644 index 0000000..52e0429 --- /dev/null +++ b/imUrlDataAppSUI/imUrlDataAppSUI IM/Info.plist @@ -0,0 +1,13 @@ + + + + + NSExtension + + NSExtensionMainStoryboard + MainInterface + NSExtensionPointIdentifier + com.apple.message-payload-provider + + + diff --git a/imUrlDataAppSUI/imUrlDataAppSUI IM/MessagesViewController.swift b/imUrlDataAppSUI/imUrlDataAppSUI IM/MessagesViewController.swift new file mode 100644 index 0000000..17fa2b0 --- /dev/null +++ b/imUrlDataAppSUI/imUrlDataAppSUI IM/MessagesViewController.swift @@ -0,0 +1,66 @@ +// +// MessagesViewController.swift +// imUrlDataAppSUI IM +// +// Created by AndyDent on 8/6/2022. +// + +import UIKit +import Messages + +class MessagesViewController: MSMessagesAppViewController { + + override func viewDidLoad() { + super.viewDidLoad() + // Do any additional setup after loading the view. + } + + // MARK: - Conversation Handling + + override func willBecomeActive(with conversation: MSConversation) { + // Called when the extension is about to move from the inactive to active state. + // This will happen when the extension is about to present UI. + + // Use this method to configure the extension and restore previously stored state. + } + + override func didResignActive(with conversation: MSConversation) { + // Called when the extension is about to move from the active to inactive state. + // This will happen when the user dismisses the extension, changes to a different + // conversation or quits Messages. + + // Use this method to release shared resources, save user data, invalidate timers, + // and store enough state information to restore your extension to its current state + // in case it is terminated later. + } + + override func didReceive(_ message: MSMessage, conversation: MSConversation) { + // Called when a message arrives that was generated by another instance of this + // extension on a remote device. + + // Use this method to trigger UI updates in response to the message. + } + + override func didStartSending(_ message: MSMessage, conversation: MSConversation) { + // Called when the user taps the send button. + } + + override func didCancelSending(_ message: MSMessage, conversation: MSConversation) { + // Called when the user deletes the message without sending it. + + // Use this to clean up state related to the deleted message. + } + + override func willTransition(to presentationStyle: MSMessagesAppPresentationStyle) { + // Called before the extension transitions to a new presentation style. + + // Use this method to prepare for the change in presentation style. + } + + override func didTransition(to presentationStyle: MSMessagesAppPresentationStyle) { + // Called after the extension transitions to a new presentation style. + + // Use this method to finalize any behaviors associated with the change in presentation style. + } + +} diff --git a/imUrlDataAppSUI/imUrlDataAppSUI.xcodeproj/project.pbxproj b/imUrlDataAppSUI/imUrlDataAppSUI.xcodeproj/project.pbxproj index 25a4ea2..ba48c5e 100644 --- a/imUrlDataAppSUI/imUrlDataAppSUI.xcodeproj/project.pbxproj +++ b/imUrlDataAppSUI/imUrlDataAppSUI.xcodeproj/project.pbxproj @@ -7,13 +7,48 @@ objects = { /* Begin PBXBuildFile section */ + 42D77E6B2850EBC400B3985F /* Messages.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 42D77E6A2850EBC400B3985F /* Messages.framework */; }; + 42D77E6E2850EBC400B3985F /* MessagesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42D77E6D2850EBC400B3985F /* MessagesViewController.swift */; }; + 42D77E712850EBC400B3985F /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 42D77E6F2850EBC400B3985F /* MainInterface.storyboard */; }; + 42D77E732850EBC500B3985F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 42D77E722850EBC500B3985F /* Assets.xcassets */; }; + 42D77E772850EBC500B3985F /* imUrlDataAppSUI IM.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 42D77E682850EBC400B3985F /* imUrlDataAppSUI IM.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 42E231C02714963F0041B8CA /* imUrlDataAppSUIApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42E231BF2714963F0041B8CA /* imUrlDataAppSUIApp.swift */; }; 42E231C22714963F0041B8CA /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42E231C12714963F0041B8CA /* ContentView.swift */; }; 42E231C4271496410041B8CA /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 42E231C3271496410041B8CA /* Assets.xcassets */; }; 42E231C7271496410041B8CA /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 42E231C6271496410041B8CA /* Preview Assets.xcassets */; }; /* End PBXBuildFile section */ +/* Begin PBXContainerItemProxy section */ + 42D77E752850EBC500B3985F /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 42E231B42714963F0041B8CA /* Project object */; + proxyType = 1; + remoteGlobalIDString = 42D77E672850EBC400B3985F; + remoteInfo = "imUrlDataAppSUI IM"; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 42D77E782850EBC600B3985F /* Embed App Extensions */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 13; + files = ( + 42D77E772850EBC500B3985F /* imUrlDataAppSUI IM.appex in Embed App Extensions */, + ); + name = "Embed App Extensions"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + /* Begin PBXFileReference section */ + 42D77E682850EBC400B3985F /* imUrlDataAppSUI IM.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "imUrlDataAppSUI IM.appex"; sourceTree = BUILT_PRODUCTS_DIR; }; + 42D77E6A2850EBC400B3985F /* Messages.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Messages.framework; path = System/Library/Frameworks/Messages.framework; sourceTree = SDKROOT; }; + 42D77E6D2850EBC400B3985F /* MessagesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessagesViewController.swift; sourceTree = ""; }; + 42D77E702850EBC400B3985F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/MainInterface.storyboard; sourceTree = ""; }; + 42D77E722850EBC500B3985F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 42D77E742850EBC500B3985F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 42E231BC2714963F0041B8CA /* imUrlDataAppSUI.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = imUrlDataAppSUI.app; sourceTree = BUILT_PRODUCTS_DIR; }; 42E231BF2714963F0041B8CA /* imUrlDataAppSUIApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = imUrlDataAppSUIApp.swift; sourceTree = ""; }; 42E231C12714963F0041B8CA /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; @@ -22,6 +57,14 @@ /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ + 42D77E652850EBC400B3985F /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 42D77E6B2850EBC400B3985F /* Messages.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 42E231B92714963F0041B8CA /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -32,10 +75,31 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 42D77E692850EBC400B3985F /* Frameworks */ = { + isa = PBXGroup; + children = ( + 42D77E6A2850EBC400B3985F /* Messages.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 42D77E6C2850EBC400B3985F /* imUrlDataAppSUI IM */ = { + isa = PBXGroup; + children = ( + 42D77E6D2850EBC400B3985F /* MessagesViewController.swift */, + 42D77E6F2850EBC400B3985F /* MainInterface.storyboard */, + 42D77E722850EBC500B3985F /* Assets.xcassets */, + 42D77E742850EBC500B3985F /* Info.plist */, + ); + path = "imUrlDataAppSUI IM"; + sourceTree = ""; + }; 42E231B32714963F0041B8CA = { isa = PBXGroup; children = ( 42E231BE2714963F0041B8CA /* imUrlDataAppSUI */, + 42D77E6C2850EBC400B3985F /* imUrlDataAppSUI IM */, + 42D77E692850EBC400B3985F /* Frameworks */, 42E231BD2714963F0041B8CA /* Products */, ); sourceTree = ""; @@ -44,6 +108,7 @@ isa = PBXGroup; children = ( 42E231BC2714963F0041B8CA /* imUrlDataAppSUI.app */, + 42D77E682850EBC400B3985F /* imUrlDataAppSUI IM.appex */, ); name = Products; sourceTree = ""; @@ -70,6 +135,23 @@ /* End PBXGroup section */ /* Begin PBXNativeTarget section */ + 42D77E672850EBC400B3985F /* imUrlDataAppSUI IM */ = { + isa = PBXNativeTarget; + buildConfigurationList = 42D77E7B2850EBC600B3985F /* Build configuration list for PBXNativeTarget "imUrlDataAppSUI IM" */; + buildPhases = ( + 42D77E642850EBC400B3985F /* Sources */, + 42D77E652850EBC400B3985F /* Frameworks */, + 42D77E662850EBC400B3985F /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "imUrlDataAppSUI IM"; + productName = "imUrlDataAppSUI IM"; + productReference = 42D77E682850EBC400B3985F /* imUrlDataAppSUI IM.appex */; + productType = "com.apple.product-type.app-extension.messages"; + }; 42E231BB2714963F0041B8CA /* imUrlDataAppSUI */ = { isa = PBXNativeTarget; buildConfigurationList = 42E231CA271496410041B8CA /* Build configuration list for PBXNativeTarget "imUrlDataAppSUI" */; @@ -77,10 +159,12 @@ 42E231B82714963F0041B8CA /* Sources */, 42E231B92714963F0041B8CA /* Frameworks */, 42E231BA2714963F0041B8CA /* Resources */, + 42D77E782850EBC600B3985F /* Embed App Extensions */, ); buildRules = ( ); dependencies = ( + 42D77E762850EBC500B3985F /* PBXTargetDependency */, ); name = imUrlDataAppSUI; productName = imUrlDataAppSUI; @@ -94,9 +178,12 @@ isa = PBXProject; attributes = { BuildIndependentTargetsInParallel = 1; - LastSwiftUpdateCheck = 1300; + LastSwiftUpdateCheck = 1340; LastUpgradeCheck = 1300; TargetAttributes = { + 42D77E672850EBC400B3985F = { + CreatedOnToolsVersion = 13.4; + }; 42E231BB2714963F0041B8CA = { CreatedOnToolsVersion = 13.0; }; @@ -116,11 +203,21 @@ projectRoot = ""; targets = ( 42E231BB2714963F0041B8CA /* imUrlDataAppSUI */, + 42D77E672850EBC400B3985F /* imUrlDataAppSUI IM */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ + 42D77E662850EBC400B3985F /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 42D77E732850EBC500B3985F /* Assets.xcassets in Resources */, + 42D77E712850EBC400B3985F /* MainInterface.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 42E231BA2714963F0041B8CA /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -133,6 +230,14 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ + 42D77E642850EBC400B3985F /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 42D77E6E2850EBC400B3985F /* MessagesViewController.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 42E231B82714963F0041B8CA /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -144,7 +249,80 @@ }; /* End PBXSourcesBuildPhase section */ +/* Begin PBXTargetDependency section */ + 42D77E762850EBC500B3985F /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 42D77E672850EBC400B3985F /* imUrlDataAppSUI IM */; + targetProxy = 42D77E752850EBC500B3985F /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 42D77E6F2850EBC400B3985F /* MainInterface.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 42D77E702850EBC400B3985F /* Base */, + ); + name = MainInterface.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + /* Begin XCBuildConfiguration section */ + 42D77E792850EBC600B3985F /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = "iMessage App Icon"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 7MXY472HBV; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = "imUrlDataAppSUI IM/Info.plist"; + INFOPLIST_KEY_CFBundleDisplayName = "imUrlDataAppSUI IM"; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + IPHONEOS_DEPLOYMENT_TARGET = 15.5; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = "test.aussie.imUrlDataAppSUI.imUrlDataAppSUI-IM"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 42D77E7A2850EBC600B3985F /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = "iMessage App Icon"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 7MXY472HBV; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = "imUrlDataAppSUI IM/Info.plist"; + INFOPLIST_KEY_CFBundleDisplayName = "imUrlDataAppSUI IM"; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + IPHONEOS_DEPLOYMENT_TARGET = 15.5; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = "test.aussie.imUrlDataAppSUI.imUrlDataAppSUI-IM"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; 42E231C8271496410041B8CA /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -264,6 +442,7 @@ 42E231CB271496410041B8CA /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_STYLE = Automatic; @@ -294,6 +473,7 @@ 42E231CC271496410041B8CA /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_STYLE = Automatic; @@ -324,6 +504,15 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ + 42D77E7B2850EBC600B3985F /* Build configuration list for PBXNativeTarget "imUrlDataAppSUI IM" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 42D77E792850EBC600B3985F /* Debug */, + 42D77E7A2850EBC600B3985F /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 42E231B72714963F0041B8CA /* Build configuration list for PBXProject "imUrlDataAppSUI" */ = { isa = XCConfigurationList; buildConfigurations = ( From e740382d5598f869ba6a6ae0031b86cf68d7ece2 Mon Sep 17 00:00:00 2001 From: Andy Dent Date: Thu, 9 Jun 2022 21:31:20 +0800 Subject: [PATCH 2/7] imUrlDataAppSUI - Adding Messenger extension Target showing SwiftUI imUrlDataAppSUI IM project target added as iMessage extension (comes in with Storyboard) Maininterface.storyboard - Messages View Controller - changed label text to be a big warning that SwiftUI is not hosted UIViewController+EmbedSwiftUI.swift - added to embed SwiftUI views ContentView.swift - add to imUrlDataSAppSUI IM project as well, just for now MessagesViewController.swift - viewDidLoad - embed an instance of ContentView --- .../imUrlDataAppSUI Code Change Diary.txt | 16 +++++++- .../Base.lproj/MainInterface.storyboard | 28 ++++++++++---- .../MessagesViewController.swift | 1 + .../UIViewController+EmbedSwiftUI.swift | 37 +++++++++++++++++++ .../imUrlDataAppSUI.xcodeproj/project.pbxproj | 6 +++ 5 files changed, 78 insertions(+), 10 deletions(-) create mode 100644 imUrlDataAppSUI/imUrlDataAppSUI IM/UIViewController+EmbedSwiftUI.swift diff --git a/imUrlDataAppSUI/imUrlDataAppSUI Code Change Diary.txt b/imUrlDataAppSUI/imUrlDataAppSUI Code Change Diary.txt index 1e687d3..b8c7c4c 100644 --- a/imUrlDataAppSUI/imUrlDataAppSUI Code Change Diary.txt +++ b/imUrlDataAppSUI/imUrlDataAppSUI Code Change Diary.txt @@ -14,9 +14,21 @@ ContentView.swift - added state variables with toggles so icon opacity is dependent -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -Adding Messenger extension Target -2022-06-08 +Adding Messenger extension Target showing SwiftUI +2022-06-08..09 imUrlDataAppSUI IM project target added as iMessage extension (comes in with Storyboard) +Maininterface.storyboard +- Messages View Controller + - changed label text to be a big warning that SwiftUI is not hosted + +UIViewController+EmbedSwiftUI.swift +- added to embed SwiftUI views + +ContentView.swift +- add to imUrlDataSAppSUI IM project as well, just for now + +MessagesViewController.swift +- viewDidLoad - embed an instance of ContentView diff --git a/imUrlDataAppSUI/imUrlDataAppSUI IM/Base.lproj/MainInterface.storyboard b/imUrlDataAppSUI/imUrlDataAppSUI IM/Base.lproj/MainInterface.storyboard index 36e2d49..3baae99 100644 --- a/imUrlDataAppSUI/imUrlDataAppSUI IM/Base.lproj/MainInterface.storyboard +++ b/imUrlDataAppSUI/imUrlDataAppSUI IM/Base.lproj/MainInterface.storyboard @@ -1,37 +1,49 @@ - + + - + + - + - - + + + + + + - + + + + + + diff --git a/imUrlDataAppSUI/imUrlDataAppSUI IM/MessagesViewController.swift b/imUrlDataAppSUI/imUrlDataAppSUI IM/MessagesViewController.swift index 17fa2b0..3c2194c 100644 --- a/imUrlDataAppSUI/imUrlDataAppSUI IM/MessagesViewController.swift +++ b/imUrlDataAppSUI/imUrlDataAppSUI IM/MessagesViewController.swift @@ -13,6 +13,7 @@ class MessagesViewController: MSMessagesAppViewController { override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. + addSubSwiftUIView(ContentView(), to: view) } // MARK: - Conversation Handling diff --git a/imUrlDataAppSUI/imUrlDataAppSUI IM/UIViewController+EmbedSwiftUI.swift b/imUrlDataAppSUI/imUrlDataAppSUI IM/UIViewController+EmbedSwiftUI.swift new file mode 100644 index 0000000..262ea48 --- /dev/null +++ b/imUrlDataAppSUI/imUrlDataAppSUI IM/UIViewController+EmbedSwiftUI.swift @@ -0,0 +1,37 @@ +// +// SwiftUI hosting code courtesy https://www.avanderlee.com/swiftui/integrating-swiftui-with-uikit/ + +import UIKit +import SwiftUI + +extension UIViewController { + + /// Add a SwiftUI `View` as a child of the input `UIView`. + /// - Parameters: + /// - swiftUIView: The SwiftUI `View` to add as a child. + /// - view: The `UIView` instance to which the view should be added. + @available(iOS 14, *) // setting 14 as a bettr bare minimum + func addSubSwiftUIView(_ swiftUIView: Content, to view: UIView) where Content : View { + let hostingController = UIHostingController(rootView: swiftUIView) + + /// Add as a child of the current view controller. + addChild(hostingController) + + /// Add the SwiftUI view to the view controller view hierarchy. + view.addSubview(hostingController.view) + + /// Setup the contraints to update the SwiftUI view boundaries. + hostingController.view.translatesAutoresizingMaskIntoConstraints = false + let constraints = [ + hostingController.view.topAnchor.constraint(equalTo: view.topAnchor), + hostingController.view.leftAnchor.constraint(equalTo: view.leftAnchor), + view.bottomAnchor.constraint(equalTo: hostingController.view.bottomAnchor), + view.rightAnchor.constraint(equalTo: hostingController.view.rightAnchor) + ] + + NSLayoutConstraint.activate(constraints) + + /// Notify the hosting controller that it has been moved to the current view controller. + hostingController.didMove(toParent: self) + } +} diff --git a/imUrlDataAppSUI/imUrlDataAppSUI.xcodeproj/project.pbxproj b/imUrlDataAppSUI/imUrlDataAppSUI.xcodeproj/project.pbxproj index ba48c5e..503dfeb 100644 --- a/imUrlDataAppSUI/imUrlDataAppSUI.xcodeproj/project.pbxproj +++ b/imUrlDataAppSUI/imUrlDataAppSUI.xcodeproj/project.pbxproj @@ -12,6 +12,8 @@ 42D77E712850EBC400B3985F /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 42D77E6F2850EBC400B3985F /* MainInterface.storyboard */; }; 42D77E732850EBC500B3985F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 42D77E722850EBC500B3985F /* Assets.xcassets */; }; 42D77E772850EBC500B3985F /* imUrlDataAppSUI IM.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 42D77E682850EBC400B3985F /* imUrlDataAppSUI IM.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + 42D77E7D28521D1900B3985F /* UIViewController+EmbedSwiftUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42D77E7C28521D1900B3985F /* UIViewController+EmbedSwiftUI.swift */; }; + 42D77E7E28522B7000B3985F /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42E231C12714963F0041B8CA /* ContentView.swift */; }; 42E231C02714963F0041B8CA /* imUrlDataAppSUIApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42E231BF2714963F0041B8CA /* imUrlDataAppSUIApp.swift */; }; 42E231C22714963F0041B8CA /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42E231C12714963F0041B8CA /* ContentView.swift */; }; 42E231C4271496410041B8CA /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 42E231C3271496410041B8CA /* Assets.xcassets */; }; @@ -49,6 +51,7 @@ 42D77E702850EBC400B3985F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/MainInterface.storyboard; sourceTree = ""; }; 42D77E722850EBC500B3985F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 42D77E742850EBC500B3985F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 42D77E7C28521D1900B3985F /* UIViewController+EmbedSwiftUI.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIViewController+EmbedSwiftUI.swift"; sourceTree = ""; }; 42E231BC2714963F0041B8CA /* imUrlDataAppSUI.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = imUrlDataAppSUI.app; sourceTree = BUILT_PRODUCTS_DIR; }; 42E231BF2714963F0041B8CA /* imUrlDataAppSUIApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = imUrlDataAppSUIApp.swift; sourceTree = ""; }; 42E231C12714963F0041B8CA /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; @@ -86,6 +89,7 @@ 42D77E6C2850EBC400B3985F /* imUrlDataAppSUI IM */ = { isa = PBXGroup; children = ( + 42D77E7C28521D1900B3985F /* UIViewController+EmbedSwiftUI.swift */, 42D77E6D2850EBC400B3985F /* MessagesViewController.swift */, 42D77E6F2850EBC400B3985F /* MainInterface.storyboard */, 42D77E722850EBC500B3985F /* Assets.xcassets */, @@ -235,6 +239,8 @@ buildActionMask = 2147483647; files = ( 42D77E6E2850EBC400B3985F /* MessagesViewController.swift in Sources */, + 42D77E7D28521D1900B3985F /* UIViewController+EmbedSwiftUI.swift in Sources */, + 42D77E7E28522B7000B3985F /* ContentView.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; From 7db1b773e25238c2b3eaa736a6517df1745cccd1 Mon Sep 17 00:00:00 2001 From: Andy Dent Date: Sat, 6 Aug 2022 11:18:59 +0800 Subject: [PATCH 3/7] imUrlDataApp - Refactor Message Sending from App for easy reuse in other sample ViewController.swift - var messager added - displayMessageInterface and messageComposeViewController moved to MessageComposerInApp - viewDidLoad use messager to set state if !canSendText - onAppSendButton call messager to display --- .../imUrlDataApp Code Change Diary.txt | 16 ++++- .../imUrlDataApp.xcodeproj/project.pbxproj | 4 ++ .../imUrlDataApp/MessageComposerInApp.swift | 64 +++++++++++++++++++ .../imUrlDataApp/ViewController.swift | 56 ++-------------- 4 files changed, 87 insertions(+), 53 deletions(-) create mode 100644 imUrlDataApp/imUrlDataApp/MessageComposerInApp.swift diff --git a/imUrlDataApp/imUrlDataApp Code Change Diary.txt b/imUrlDataApp/imUrlDataApp Code Change Diary.txt index dca7653..8b151f3 100644 --- a/imUrlDataApp/imUrlDataApp Code Change Diary.txt +++ b/imUrlDataApp/imUrlDataApp Code Change Diary.txt @@ -117,4 +117,18 @@ ViewController.swift - add protocol MFMessageComposeViewControllerDelegate - appSendButton added and connected - onAppSendButton added to invoke displayMessageInterface -- displayMessageInterface added \ No newline at end of file +- displayMessageInterface added + + +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +Refactor Message Sending from App for easy reuse in other sample +2022-08-05 + +ViewController.swift +- var messager added +- displayMessageInterface and messageComposeViewController moved to MessageComposerInApp +- viewDidLoad use messager to set state if !canSendText +- onAppSendButton call messager to display + +MessageComposerInApp.swift +- added as NSObject, MFMessageComposeViewControllerDelegate \ No newline at end of file diff --git a/imUrlDataApp/imUrlDataApp.xcodeproj/project.pbxproj b/imUrlDataApp/imUrlDataApp.xcodeproj/project.pbxproj index d151d4e..19c4a3a 100644 --- a/imUrlDataApp/imUrlDataApp.xcodeproj/project.pbxproj +++ b/imUrlDataApp/imUrlDataApp.xcodeproj/project.pbxproj @@ -19,6 +19,7 @@ 42199E9C21F1CA5700D4377E /* imUrlDataApp IM.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 42199E8D21F1CA5600D4377E /* imUrlDataApp IM.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 42199EA521F22CA400D4377E /* SharedData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42199EA421F22CA400D4377E /* SharedData.swift */; }; 42199EA621F22CA400D4377E /* SharedData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42199EA421F22CA400D4377E /* SharedData.swift */; }; + 4276811F289D2D8B008FDF6C /* MessageComposerInApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4276811E289D2D8B008FDF6C /* MessageComposerInApp.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -62,6 +63,7 @@ 42199EA121F22A1300D4377E /* imUrlDataApp.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = imUrlDataApp.entitlements; sourceTree = ""; }; 42199EA221F22A1700D4377E /* imUrlDataApp IM.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "imUrlDataApp IM.entitlements"; sourceTree = ""; }; 42199EA421F22CA400D4377E /* SharedData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharedData.swift; sourceTree = ""; }; + 4276811E289D2D8B008FDF6C /* MessageComposerInApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageComposerInApp.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -113,6 +115,7 @@ 4201A9D621F0763C0043E369 /* Assets.xcassets */, 4201A9D821F0763C0043E369 /* LaunchScreen.storyboard */, 4201A9DB21F0763C0043E369 /* Info.plist */, + 4276811E289D2D8B008FDF6C /* MessageComposerInApp.swift */, ); path = imUrlDataApp; sourceTree = ""; @@ -260,6 +263,7 @@ files = ( 42199EA521F22CA400D4377E /* SharedData.swift in Sources */, 4201A9D221F0763B0043E369 /* ViewController.swift in Sources */, + 4276811F289D2D8B008FDF6C /* MessageComposerInApp.swift in Sources */, 4201A9D021F0763B0043E369 /* AppDelegate.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/imUrlDataApp/imUrlDataApp/MessageComposerInApp.swift b/imUrlDataApp/imUrlDataApp/MessageComposerInApp.swift new file mode 100644 index 0000000..e98768d --- /dev/null +++ b/imUrlDataApp/imUrlDataApp/MessageComposerInApp.swift @@ -0,0 +1,64 @@ +// +// MessageComposerInApp.swift +// imUrlDataApp +// +// Created by AndyDent on 5/8/2022. +// Copyright © 2022 Touchgram Pty Ltd. All rights reserved. +// + +import Foundation +import MessageUI +import Messages + +class MessageComposingHelper: NSObject, MFMessageComposeViewControllerDelegate { + func canSendText() -> Bool { + MFMessageComposeViewController.canSendText() + } + + func displayMessageInterface(onVC vc: UIViewController) { + let composeVC = MFMessageComposeViewController() + composeVC.messageComposeDelegate = self + + // Configure the fields of the interface. + composeVC.recipients = ["123456"] + composeVC.body = "Sending a custom message" + // + /* + According to + https://developer.apple.com/documentation/messageui/mfmessagecomposeviewcontroller/2213331-message + If your app has an iMessage app extension, you can display your iMessage app within the message compose view, just as you would in the Messages app. To display your iMessage app, create and assign an MSMessage object to this property. + + By default, this property is set to nil. + */ + if #available(iOS 10, *) { // necessary if clause to make XCode happy to use composeVC.message + let message = MSMessage(session: MSSession()) + // fake building a smiley using hardcoded stuff to match MessagesViewController.send + guard var urlComps = URLComponents(string:"data:,") else { + fatalError("Invalid base URL") + } + urlComps.queryItems = [URLQueryItem(name:"mood", value:"happy")] + message.url = urlComps.url + composeVC.message = message + } + + // Present the view controller modally. + if MFMessageComposeViewController.canSendText() { + vc.present(composeVC, animated: true, completion: nil) + } else { + print("Can't send messages.") + } + } + + //MARK - conform to MFMessageComposeViewControllerDelegate + func messageComposeViewController(_ controller: MFMessageComposeViewController, + didFinishWith result: MessageComposeResult) { + // Check the result or perform other tasks. + let msgStr = result == .cancelled ? + "Cancelled" : result == .failed ? + "Failed" : + "Sent" + print(msgStr) + // Dismiss the message compose view controller. + controller.dismiss(animated: true, completion: nil) + } +} diff --git a/imUrlDataApp/imUrlDataApp/ViewController.swift b/imUrlDataApp/imUrlDataApp/ViewController.swift index 5a6aedf..c1b0b06 100644 --- a/imUrlDataApp/imUrlDataApp/ViewController.swift +++ b/imUrlDataApp/imUrlDataApp/ViewController.swift @@ -7,8 +7,6 @@ // import UIKit -import MessageUI -import Messages extension UISwitch { func toggle() { @@ -16,7 +14,7 @@ extension UISwitch { } } -class ViewController: UIViewController, MFMessageComposeViewControllerDelegate { +class ViewController: UIViewController { @IBOutlet fileprivate weak var happyBtn: UIButton! @IBOutlet fileprivate weak var quizzicalBtn: UIButton! @@ -31,6 +29,7 @@ class ViewController: UIViewController, MFMessageComposeViewControllerDelegate { lazy var toggles = [happySwitch, quizzicalSwitch, distraughtSwitch, angrySwitch] lazy var buttons = [happyBtn, quizzicalBtn, distraughtBtn, angryBtn] var lastToggled : controlIndexes? = nil + var messager = MessageComposingHelper() // array of flags instead of storing state in the switch so can easily save and load var enabled = [Bool]() @@ -48,7 +47,7 @@ class ViewController: UIViewController, MFMessageComposeViewControllerDelegate { lastToggled = isOn ? lastToggled : controlIndexes(rawValue: i) } } - if !MFMessageComposeViewController.canSendText() { + if !messager.canSendText() { appSendButton.isEnabled = false appSendButton.titleLabel?.text = "Not allowed to send texts" } @@ -117,55 +116,8 @@ class ViewController: UIViewController, MFMessageComposeViewControllerDelegate { /// see Readme and https://developer.apple.com/documentation/messageui/mfmessagecomposeviewcontroller @IBAction func onAppSendButton(_ sender: Any) { - displayMessageInterface() + messager.displayMessageInterface(onVC: self) } - - func displayMessageInterface() { - let composeVC = MFMessageComposeViewController() - composeVC.messageComposeDelegate = self - - // Configure the fields of the interface. - composeVC.recipients = ["123456"] - composeVC.body = "Sending a custom message" - // - /* - According to - https://developer.apple.com/documentation/messageui/mfmessagecomposeviewcontroller/2213331-message - If your app has an iMessage app extension, you can display your iMessage app within the message compose view, just as you would in the Messages app. To display your iMessage app, create and assign an MSMessage object to this property. - By default, this property is set to nil. - */ - if #available(iOS 10, *) { // necessary if clause to make XCode happy to use composeVC.message - let message = MSMessage(session: MSSession()) - // fake building a smiley using hardcoded stuff to match MessagesViewController.send - guard var urlComps = URLComponents(string:"data:,") else { - fatalError("Invalid base URL") - } - urlComps.queryItems = [URLQueryItem(name:"mood", value:"happy")] - message.url = urlComps.url - composeVC.message = message - } - - // Present the view controller modally. - if MFMessageComposeViewController.canSendText() { - self.present(composeVC, animated: true, completion: nil) - } else { - print("Can't send messages.") - } - } - - //MARK - conform to MFMessageComposeViewControllerDelegate - func messageComposeViewController(_ controller: MFMessageComposeViewController, - didFinishWith result: MessageComposeResult) { - // Check the result or perform other tasks. - let msgStr = result == .cancelled ? - "Cancelled" : result == .failed ? - "Failed" : - "Sent" - print(msgStr) - // Dismiss the message compose view controller. - controller.dismiss(animated: true, completion: nil) - - } } From 9e30a15dd0394fb5eec708a849e8b94323f3c0ba Mon Sep 17 00:00:00 2001 From: Andy Dent Date: Sat, 6 Aug 2022 12:03:22 +0800 Subject: [PATCH 4/7] imUrlDataApp - Fix app sender to send last-tapped emoji (was always "happy") MessagesViewController.swift - Mood enum moved out to its own file - local let moodKey moved to Mood enum - subscript added MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit MessageComposingHelper - displayMessageInterface take a mood param instead of using hardcoded strings ViewController - onAppSendButton pass mood to displayMessageInterface - buttonFor(mood) helper added - tap(mood) added - viewDidLoad - call tap(mood) to get highlighted - set button isEnabled state (now we're not using them to toggle back on, should only be tappable when enabled) - onHappy…Angry - call tap(mood:) instead of toggle, so tapping emoji sets the one to send - matchButtonsToToggles - set isEnabled state --- imUrlDataApp/Common/Mood.swift | 24 ++++++++++ .../imUrlDataApp Code Change Diary.txt | 25 +++++++++- .../MessagesViewController.swift | 13 +----- .../imUrlDataApp.xcodeproj/project.pbxproj | 6 +++ .../imUrlDataApp/MessageComposerInApp.swift | 5 +- .../imUrlDataApp/ViewController.swift | 46 ++++++++++++++++--- 6 files changed, 99 insertions(+), 20 deletions(-) create mode 100644 imUrlDataApp/Common/Mood.swift diff --git a/imUrlDataApp/Common/Mood.swift b/imUrlDataApp/Common/Mood.swift new file mode 100644 index 0000000..2e138b9 --- /dev/null +++ b/imUrlDataApp/Common/Mood.swift @@ -0,0 +1,24 @@ +// +// Mood.swift +// imUrlDataApp +// +// Created by AndyDent on 6/8/2022. +// Copyright © 2022 Touchgram Pty Ltd. All rights reserved. +// + +import Foundation + +enum Mood : String, CaseIterable { + case happy + case quizzical + case distraught + case angry + + static let moodKey = "mood" + + static subscript(i: Int) -> Mood? { + guard i >= 0 && i < 4 else {return nil} + return Mood.allCases[i] + } + +} diff --git a/imUrlDataApp/imUrlDataApp Code Change Diary.txt b/imUrlDataApp/imUrlDataApp Code Change Diary.txt index 8b151f3..cf8ee9f 100644 --- a/imUrlDataApp/imUrlDataApp Code Change Diary.txt +++ b/imUrlDataApp/imUrlDataApp Code Change Diary.txt @@ -131,4 +131,27 @@ ViewController.swift - onAppSendButton call messager to display MessageComposerInApp.swift -- added as NSObject, MFMessageComposeViewControllerDelegate \ No newline at end of file +- added as NSObject, MFMessageComposeViewControllerDelegate + + +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +Fix app sender to send last-tapped emoji (was always "happy") +2022-08-06 + +MessagesViewController.swift +- Mood enum moved out to its own file +- local let moodKey moved to Mood enum +- subscript added + +MessageComposingHelper +- displayMessageInterface take a mood param instead of using hardcoded strings + +ViewController +- onAppSendButton pass mood to displayMessageInterface +- buttonFor(mood) helper added +- tap(mood) added +- viewDidLoad + - call tap(mood) to get highlighted + - set button isEnabled state (now we're not using them to toggle back on, should only be tappable when enabled) +- onHappy…Angry - call tap(mood:) instead of toggle, so tapping emoji sets the one to send +- matchButtonsToToggles - set isEnabled state diff --git a/imUrlDataApp/imUrlDataApp IM/MessagesViewController.swift b/imUrlDataApp/imUrlDataApp IM/MessagesViewController.swift index 88028bf..aa582db 100644 --- a/imUrlDataApp/imUrlDataApp IM/MessagesViewController.swift +++ b/imUrlDataApp/imUrlDataApp IM/MessagesViewController.swift @@ -10,14 +10,6 @@ import UIKit import os import Messages - -enum Mood : String { - case happy - case quizzical - case distraught - case angry -} - class MessagesViewController: MSMessagesAppViewController { @IBOutlet fileprivate weak var happyBtn: UIButton! @@ -27,7 +19,6 @@ class MessagesViewController: MSMessagesAppViewController { @IBOutlet fileprivate weak var statusLabel: UILabel! var receivedMood:Mood? = nil - let moodKey = "mood" let responseKey = "respondingTo" let senderTimestampKey = "sentTS" var enabled = [Bool]() @@ -62,7 +53,7 @@ class MessagesViewController: MSMessagesAppViewController { } os_log("hasIncoming parsing message") guard let comps = URLComponents(url: url, resolvingAgainstBaseURL: true) else { return } - if let msgMood = comps.queryItems?.first(where: { $0.name == moodKey })?.value { + if let msgMood = comps.queryItems?.first(where: { $0.name == Mood.moodKey })?.value { if let parsedMood = Mood(rawValue: msgMood) { receivedMood = parsedMood statusLabel.text = "Respond to '\(msgMood)'" @@ -155,7 +146,7 @@ class MessagesViewController: MSMessagesAppViewController { fatalError("Invalid base URL") } var qi = [ - URLQueryItem(name:moodKey, value:mood.rawValue) + URLQueryItem(name: Mood.moodKey, value:mood.rawValue) ] if receivedMood != nil { qi.append(URLQueryItem(name:responseKey, value:receivedMood!.rawValue)) diff --git a/imUrlDataApp/imUrlDataApp.xcodeproj/project.pbxproj b/imUrlDataApp/imUrlDataApp.xcodeproj/project.pbxproj index 19c4a3a..0777320 100644 --- a/imUrlDataApp/imUrlDataApp.xcodeproj/project.pbxproj +++ b/imUrlDataApp/imUrlDataApp.xcodeproj/project.pbxproj @@ -12,6 +12,8 @@ 4201A9D521F0763B0043E369 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4201A9D321F0763B0043E369 /* Main.storyboard */; }; 4201A9D721F0763C0043E369 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4201A9D621F0763C0043E369 /* Assets.xcassets */; }; 4201A9DA21F0763C0043E369 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4201A9D821F0763C0043E369 /* LaunchScreen.storyboard */; }; + 42089152289E15FB0036C790 /* Mood.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42089151289E15FB0036C790 /* Mood.swift */; }; + 42089153289E15FB0036C790 /* Mood.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42089151289E15FB0036C790 /* Mood.swift */; }; 42199E9021F1CA5600D4377E /* Messages.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 42199E8F21F1CA5600D4377E /* Messages.framework */; }; 42199E9321F1CA5600D4377E /* MessagesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42199E9221F1CA5600D4377E /* MessagesViewController.swift */; }; 42199E9621F1CA5600D4377E /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 42199E9421F1CA5600D4377E /* MainInterface.storyboard */; }; @@ -54,6 +56,7 @@ 4201A9D621F0763C0043E369 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 4201A9D921F0763C0043E369 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 4201A9DB21F0763C0043E369 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 42089151289E15FB0036C790 /* Mood.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Mood.swift; sourceTree = ""; }; 42199E8D21F1CA5600D4377E /* imUrlDataApp IM.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "imUrlDataApp IM.appex"; sourceTree = BUILT_PRODUCTS_DIR; }; 42199E8F21F1CA5600D4377E /* Messages.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Messages.framework; path = System/Library/Frameworks/Messages.framework; sourceTree = SDKROOT; }; 42199E9221F1CA5600D4377E /* MessagesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessagesViewController.swift; sourceTree = ""; }; @@ -144,6 +147,7 @@ isa = PBXGroup; children = ( 42199EA421F22CA400D4377E /* SharedData.swift */, + 42089151289E15FB0036C790 /* Mood.swift */, ); path = Common; sourceTree = ""; @@ -261,6 +265,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 42089152289E15FB0036C790 /* Mood.swift in Sources */, 42199EA521F22CA400D4377E /* SharedData.swift in Sources */, 4201A9D221F0763B0043E369 /* ViewController.swift in Sources */, 4276811F289D2D8B008FDF6C /* MessageComposerInApp.swift in Sources */, @@ -273,6 +278,7 @@ buildActionMask = 2147483647; files = ( 42199EA621F22CA400D4377E /* SharedData.swift in Sources */, + 42089153289E15FB0036C790 /* Mood.swift in Sources */, 42199E9321F1CA5600D4377E /* MessagesViewController.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/imUrlDataApp/imUrlDataApp/MessageComposerInApp.swift b/imUrlDataApp/imUrlDataApp/MessageComposerInApp.swift index e98768d..8daada8 100644 --- a/imUrlDataApp/imUrlDataApp/MessageComposerInApp.swift +++ b/imUrlDataApp/imUrlDataApp/MessageComposerInApp.swift @@ -11,11 +11,12 @@ import MessageUI import Messages class MessageComposingHelper: NSObject, MFMessageComposeViewControllerDelegate { + func canSendText() -> Bool { MFMessageComposeViewController.canSendText() } - func displayMessageInterface(onVC vc: UIViewController) { + func displayMessageInterface(onVC vc: UIViewController, mood:Mood) { let composeVC = MFMessageComposeViewController() composeVC.messageComposeDelegate = self @@ -36,7 +37,7 @@ class MessageComposingHelper: NSObject, MFMessageComposeViewControllerDelegate { guard var urlComps = URLComponents(string:"data:,") else { fatalError("Invalid base URL") } - urlComps.queryItems = [URLQueryItem(name:"mood", value:"happy")] + urlComps.queryItems = [URLQueryItem(name: Mood.moodKey, value:mood.rawValue)] message.url = urlComps.url composeVC.message = message } diff --git a/imUrlDataApp/imUrlDataApp/ViewController.swift b/imUrlDataApp/imUrlDataApp/ViewController.swift index c1b0b06..45c6d2a 100644 --- a/imUrlDataApp/imUrlDataApp/ViewController.swift +++ b/imUrlDataApp/imUrlDataApp/ViewController.swift @@ -26,10 +26,11 @@ class ViewController: UIViewController { @IBOutlet fileprivate weak var angrySwitch: UISwitch! @IBOutlet fileprivate weak var appSendButton: UIButton! - lazy var toggles = [happySwitch, quizzicalSwitch, distraughtSwitch, angrySwitch] + lazy var toggles = [happySwitch, quizzicalSwitch, distraughtSwitch, angrySwitch] // maintains same order as Mood lazy var buttons = [happyBtn, quizzicalBtn, distraughtBtn, angryBtn] var lastToggled : controlIndexes? = nil var messager = MessageComposingHelper() + var lastTappedMood = Mood.happy // array of flags instead of storing state in the switch so can easily save and load var enabled = [Bool]() @@ -40,25 +41,58 @@ class ViewController: UIViewController { enabled = SharedData.current.loadEnabled() // initial loading loop - if read at least one false if enabled.contains(false) { + var firstOn: Mood? = nil for (i, isOn) in enabled.enumerated() { + if isOn && firstOn == nil { + firstOn = Mood[i] + } buttons[i]?.alpha = isOn ? 1.0 : 0.3 + buttons[i]?.isEnabled = isOn toggles[i]?.isOn = isOn // bit hacky, just set lastToggled to last off lastToggled = isOn ? lastToggled : controlIndexes(rawValue: i) } + lastTappedMood = firstOn ?? .happy } if !messager.canSendText() { appSendButton.isEnabled = false appSendButton.titleLabel?.text = "Not allowed to send texts" } + tap(mood: lastTappedMood) // just to get it highlighted } func matchButtonsToToggles() { for (i, isOn) in enabled.enumerated() { buttons[i]?.alpha = isOn ? 1.0 : 0.3 + buttons[i]?.isEnabled = isOn } } + + func buttonFor(mood: Mood) -> UIButton { + switch mood { + case .happy: + return happyBtn + case .quizzical: + return quizzicalBtn + case .distraught: + return distraughtBtn + case .angry: + return angryBtn + } + } + + func tap(mood: Mood) { + buttonFor(mood: lastTappedMood).layer.borderWidth = 0 + buttonFor(mood: mood).layer.borderWidth = 4 + if #available(iOS 13.0, *) { + buttonFor(mood: mood).layer.borderColor = CGColor(red: 099, green: 0.6, blue: 0.05, alpha: 1.0) + } else { + // Fallback on earlier versions + } + lastTappedMood = mood + } + func allOff() -> Bool { return !enabled.contains(true) } @@ -98,25 +132,25 @@ class ViewController: UIViewController { @IBAction func onHappy(_ sender: Any) { - toggle(.happy) + tap(mood: .happy) } @IBAction func onQuizzical(_ sender: Any) { - toggle(.quizzical) + tap(mood: .quizzical) } @IBAction func onDistraught(_ sender: Any) { - toggle(.distraught) + tap(mood: .distraught) } @IBAction func onAngry(_ sender: Any) { - toggle(.angry) + tap(mood: .angry) } /// see Readme and https://developer.apple.com/documentation/messageui/mfmessagecomposeviewcontroller @IBAction func onAppSendButton(_ sender: Any) { - messager.displayMessageInterface(onVC: self) + messager.displayMessageInterface(onVC: self, mood: lastTappedMood) } } From 6f7283b427b913d1e0757b51bf595f577c4301a2 Mon Sep 17 00:00:00 2001 From: Andy Dent Date: Mon, 8 Aug 2022 12:34:01 +0800 Subject: [PATCH 5/7] imUrlDataApp renamed MessageComposingHelper file to match class --- imUrlDataApp/imUrlDataApp.xcodeproj/project.pbxproj | 8 ++++---- ...geComposerInApp.swift => MessageComposingHelper.swift} | 0 2 files changed, 4 insertions(+), 4 deletions(-) rename imUrlDataApp/imUrlDataApp/{MessageComposerInApp.swift => MessageComposingHelper.swift} (100%) diff --git a/imUrlDataApp/imUrlDataApp.xcodeproj/project.pbxproj b/imUrlDataApp/imUrlDataApp.xcodeproj/project.pbxproj index 0777320..a7d3def 100644 --- a/imUrlDataApp/imUrlDataApp.xcodeproj/project.pbxproj +++ b/imUrlDataApp/imUrlDataApp.xcodeproj/project.pbxproj @@ -21,7 +21,7 @@ 42199E9C21F1CA5700D4377E /* imUrlDataApp IM.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 42199E8D21F1CA5600D4377E /* imUrlDataApp IM.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 42199EA521F22CA400D4377E /* SharedData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42199EA421F22CA400D4377E /* SharedData.swift */; }; 42199EA621F22CA400D4377E /* SharedData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42199EA421F22CA400D4377E /* SharedData.swift */; }; - 4276811F289D2D8B008FDF6C /* MessageComposerInApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4276811E289D2D8B008FDF6C /* MessageComposerInApp.swift */; }; + 4276811F289D2D8B008FDF6C /* MessageComposingHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4276811E289D2D8B008FDF6C /* MessageComposingHelper.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -66,7 +66,7 @@ 42199EA121F22A1300D4377E /* imUrlDataApp.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = imUrlDataApp.entitlements; sourceTree = ""; }; 42199EA221F22A1700D4377E /* imUrlDataApp IM.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "imUrlDataApp IM.entitlements"; sourceTree = ""; }; 42199EA421F22CA400D4377E /* SharedData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharedData.swift; sourceTree = ""; }; - 4276811E289D2D8B008FDF6C /* MessageComposerInApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageComposerInApp.swift; sourceTree = ""; }; + 4276811E289D2D8B008FDF6C /* MessageComposingHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageComposingHelper.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -118,7 +118,7 @@ 4201A9D621F0763C0043E369 /* Assets.xcassets */, 4201A9D821F0763C0043E369 /* LaunchScreen.storyboard */, 4201A9DB21F0763C0043E369 /* Info.plist */, - 4276811E289D2D8B008FDF6C /* MessageComposerInApp.swift */, + 4276811E289D2D8B008FDF6C /* MessageComposingHelper.swift */, ); path = imUrlDataApp; sourceTree = ""; @@ -268,7 +268,7 @@ 42089152289E15FB0036C790 /* Mood.swift in Sources */, 42199EA521F22CA400D4377E /* SharedData.swift in Sources */, 4201A9D221F0763B0043E369 /* ViewController.swift in Sources */, - 4276811F289D2D8B008FDF6C /* MessageComposerInApp.swift in Sources */, + 4276811F289D2D8B008FDF6C /* MessageComposingHelper.swift in Sources */, 4201A9D021F0763B0043E369 /* AppDelegate.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/imUrlDataApp/imUrlDataApp/MessageComposerInApp.swift b/imUrlDataApp/imUrlDataApp/MessageComposingHelper.swift similarity index 100% rename from imUrlDataApp/imUrlDataApp/MessageComposerInApp.swift rename to imUrlDataApp/imUrlDataApp/MessageComposingHelper.swift From 5c3bbe3fa248ed8cdb64a5560361c10d06c3e125 Mon Sep 17 00:00:00 2001 From: Andy Dent Date: Mon, 8 Aug 2022 12:42:30 +0800 Subject: [PATCH 6/7] imUrlDatAppSUI - Fix app sender and include refactoring from imUrlDataApp (still broken) MessagesViewController.swift - Mood enum moved out to its own file - local let moodKey moved to Mood enum - Mood subscript added MessagePickerHost.swift - added but unfinished, mostly taking over MessageComposingHelper role plus the embedding. --- imUrlDataAppSUI/Common/Mood.swift | 24 +++ imUrlDataAppSUI/Common/SharedData.swift | 44 ++++++ imUrlDataAppSUI/README.md | 11 +- .../imUrlDataAppSUI Code Change Diary.txt | 12 ++ .../Base.lproj/MainInterface.storyboard | 120 +++++++++++--- imUrlDataAppSUI/imUrlDataAppSUI IM/Info.plist | 18 +++ .../MessagesViewController.swift | 146 +++++++++++++++++- .../UIViewController+EmbedSwiftUI.swift | 2 +- .../imUrlDataAppSUI.xcodeproj/project.pbxproj | 38 ++++- .../imUrlDataAppSUI/ContentView.swift | 10 +- .../MessageComposingHelper.swift | 31 ++++ .../imUrlDataAppSUI/MessagePickerHost.swift | 69 +++++++++ 12 files changed, 488 insertions(+), 37 deletions(-) create mode 100644 imUrlDataAppSUI/Common/Mood.swift create mode 100644 imUrlDataAppSUI/Common/SharedData.swift create mode 100644 imUrlDataAppSUI/imUrlDataAppSUI/MessageComposingHelper.swift create mode 100644 imUrlDataAppSUI/imUrlDataAppSUI/MessagePickerHost.swift diff --git a/imUrlDataAppSUI/Common/Mood.swift b/imUrlDataAppSUI/Common/Mood.swift new file mode 100644 index 0000000..2e138b9 --- /dev/null +++ b/imUrlDataAppSUI/Common/Mood.swift @@ -0,0 +1,24 @@ +// +// Mood.swift +// imUrlDataApp +// +// Created by AndyDent on 6/8/2022. +// Copyright © 2022 Touchgram Pty Ltd. All rights reserved. +// + +import Foundation + +enum Mood : String, CaseIterable { + case happy + case quizzical + case distraught + case angry + + static let moodKey = "mood" + + static subscript(i: Int) -> Mood? { + guard i >= 0 && i < 4 else {return nil} + return Mood.allCases[i] + } + +} diff --git a/imUrlDataAppSUI/Common/SharedData.swift b/imUrlDataAppSUI/Common/SharedData.swift new file mode 100644 index 0000000..9b4f2db --- /dev/null +++ b/imUrlDataAppSUI/Common/SharedData.swift @@ -0,0 +1,44 @@ +// +// SharedData.swift +// imHostingVCWithSUI +// +// Created by Andrew Dent on 18/1/19. +// Copyright © 2019 Touchgram Pty Ltd. All rights reserved. +// + +// in a more complex app this would be in a shared framework but is just one file compiled in both targets +import Foundation + +public enum controlIndexes:Int, CaseIterable { + case happy=0 + case quizzical=1 + case distraught=2 + case angry=3 +} + + +class SharedData { + /*let docsBaseURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!*/ + let docsBaseURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.com.touchgram.imHostingVCWithSUI")! + lazy var plistURL = docsBaseURL.appendingPathComponent("sharedDetails.plist") + let enabledKey = "Enabled" + + static var current = SharedData() + + func loadEnabled() -> [Bool] { + if FileManager.default.fileExists(atPath:plistURL.path) { + if let dict = NSDictionary(contentsOf:plistURL) as? [String:AnyObject] { + if let readEn = dict[enabledKey] as? [Bool] { + assert(readEn.count == controlIndexes.allCases.count) + return readEn + } + } + } + return Array(repeating: true, count: controlIndexes.allCases.count) + } + + func save(enabled:[Bool]) { + let dict = NSDictionary(dictionary: [enabledKey:enabled]) + dict.write(to: plistURL, atomically:true) + } +} diff --git a/imUrlDataAppSUI/README.md b/imUrlDataAppSUI/README.md index b875ad2..724d1f5 100644 --- a/imUrlDataAppSUI/README.md +++ b/imUrlDataAppSUI/README.md @@ -1,4 +1,13 @@ # imUrlDataAppSUI See the overall rationale in the [readme in the parent folder](../README.md). -Same as imUrlDataApp but re-implemented in SwiftUI - very early, partial attempt. \ No newline at end of file +Same as imUrlDataApp but re-implemented in SwiftUI. + +** broken work in progress ** + +Currently a bit stalled working out the use of UIViewControllerRepresentable and it's a low-priority project so may not be updated for weeks. + +## Architecture +The app side is a conventional SwiftUI app. + +The iMessage extension uses embedding. \ No newline at end of file diff --git a/imUrlDataAppSUI/imUrlDataAppSUI Code Change Diary.txt b/imUrlDataAppSUI/imUrlDataAppSUI Code Change Diary.txt index b8c7c4c..e148906 100644 --- a/imUrlDataAppSUI/imUrlDataAppSUI Code Change Diary.txt +++ b/imUrlDataAppSUI/imUrlDataAppSUI Code Change Diary.txt @@ -32,3 +32,15 @@ ContentView.swift MessagesViewController.swift - viewDidLoad - embed an instance of ContentView + +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +Fix app sender and include refactoring from imUrlDataApp (still broken) +2022-08-07 + +MessagesViewController.swift +- Mood enum moved out to its own file +- local let moodKey moved to Mood enum +- Mood subscript added + +MessagePickerHost.swift +- added but unfinished, mostly taking over MessageComposingHelper role plus the embedding. \ No newline at end of file diff --git a/imUrlDataAppSUI/imUrlDataAppSUI IM/Base.lproj/MainInterface.storyboard b/imUrlDataAppSUI/imUrlDataAppSUI IM/Base.lproj/MainInterface.storyboard index 3baae99..8093262 100644 --- a/imUrlDataAppSUI/imUrlDataAppSUI IM/Base.lproj/MainInterface.storyboard +++ b/imUrlDataAppSUI/imUrlDataAppSUI IM/Base.lproj/MainInterface.storyboard @@ -1,10 +1,12 @@ - - + + + + - + + - @@ -16,34 +18,110 @@ - - - + - - - - - - + + + + + + + + + + + + + + + + + - - - - - - diff --git a/imUrlDataAppSUI/imUrlDataAppSUI IM/Info.plist b/imUrlDataAppSUI/imUrlDataAppSUI IM/Info.plist index 52e0429..8cf9364 100644 --- a/imUrlDataAppSUI/imUrlDataAppSUI IM/Info.plist +++ b/imUrlDataAppSUI/imUrlDataAppSUI IM/Info.plist @@ -2,6 +2,24 @@ + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + imUrlDataAppSUI IM + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + XPC! + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 NSExtension NSExtensionMainStoryboard diff --git a/imUrlDataAppSUI/imUrlDataAppSUI IM/MessagesViewController.swift b/imUrlDataAppSUI/imUrlDataAppSUI IM/MessagesViewController.swift index 3c2194c..0be992c 100644 --- a/imUrlDataAppSUI/imUrlDataAppSUI IM/MessagesViewController.swift +++ b/imUrlDataAppSUI/imUrlDataAppSUI IM/MessagesViewController.swift @@ -2,55 +2,123 @@ // MessagesViewController.swift // imUrlDataAppSUI IM // -// Created by AndyDent on 8/6/2022. +// Created by Andrew Dent on 18/1/19. +// Copyright © 2019 Touchgram Pty Ltd. All rights reserved. // import UIKit +import os import Messages + class MessagesViewController: MSMessagesAppViewController { + @IBOutlet fileprivate weak var happyBtn: UIButton! + @IBOutlet fileprivate weak var quizzicalBtn: UIButton! + @IBOutlet fileprivate weak var distraughtBtn: UIButton! + @IBOutlet fileprivate weak var angryBtn: UIButton! + @IBOutlet fileprivate weak var statusLabel: UILabel! + + var receivedMood:Mood? = nil + let responseKey = "respondingTo" + let senderTimestampKey = "sentTS" + var enabled = [Bool]() + lazy var buttons = [happyBtn, quizzicalBtn, distraughtBtn, angryBtn] + + func enableButtons() { + for (i, isOn) in enabled.enumerated() { + // emoji not dimmed if disabled, still need to change appearance + // note this is a little different to main app - there we dim but they still are enabled + buttons[i]?.isEnabled = isOn + buttons[i]?.alpha = isOn ? 1.0 : 0.3 + // WEIRD SIDE-EFFECT OF BEING IN IMESSAGE + // in main app, could set button.alpha OR buttons.titleLabel?.alpha + } + //view.setNeedsDisplay() + } + override func viewDidLoad() { + os_log("viewDidLoad") super.viewDidLoad() // Do any additional setup after loading the view. - addSubSwiftUIView(ContentView(), to: view) + enabled = SharedData.current.loadEnabled() + enableButtons() } // MARK: - Conversation Handling + func hasIncoming(message: MSMessage) { + // Use this method to trigger UI updates in response to the message. + guard let url = message.url else { + os_log("hasIncoming but no message URL") + return + } + os_log("hasIncoming parsing message") + guard let comps = URLComponents(url: url, resolvingAgainstBaseURL: true) else { return } + if let msgMood = comps.queryItems?.first(where: { $0.name == Mood.moodKey })?.value { + if let parsedMood = Mood(rawValue: msgMood) { + receivedMood = parsedMood + statusLabel.text = "Respond to '\(msgMood)'" + // TODO parse the responseKey and do fancier stuff + } + } + } override func willBecomeActive(with conversation: MSConversation) { // Called when the extension is about to move from the inactive to active state. // This will happen when the extension is about to present UI. + // NOTE that means you may have launched the extension to compose a new message OR selected previous, which also hits didSelect // Use this method to configure the extension and restore previously stored state. + os_log("willBecomeActive") + } + + override func didBecomeActive(with conversation: MSConversation) { + guard let sel = conversation.selectedMessage else { + os_log("didBecomeActive with no selectedMessage in conversation") + return + } + print("didBecomeActive \(sel.debugDescription)\n URL \(sel.url?.absoluteString ?? "no URL")") + hasIncoming(message: sel) } override func didResignActive(with conversation: MSConversation) { // Called when the extension is about to move from the active to inactive state. - // This will happen when the user dismisses the extension, changes to a different + // This will happen when the user dissmises the extension, changes to a different // conversation or quits Messages. // Use this method to release shared resources, save user data, invalidate timers, // and store enough state information to restore your extension to its current state // in case it is terminated later. + os_log("didResignActive") } + override func didSelect(_ message: MSMessage, conversation: MSConversation) { + os_log("didSelect") + print("didSelect \(message.debugDescription)\n URL \(message.url?.absoluteString ?? "no URL")") + super.didSelect(message, conversation: conversation) + hasIncoming(message: message) + } + override func didReceive(_ message: MSMessage, conversation: MSConversation) { // Called when a message arrives that was generated by another instance of this - // extension on a remote device. - - // Use this method to trigger UI updates in response to the message. + // extension on a remote device. ONLY if the message arrives whilst + // this extension is active (ie: composing a new message with it) + hasIncoming(message: message) } override func didStartSending(_ message: MSMessage, conversation: MSConversation) { // Called when the user taps the send button. + os_log("didStartSending") + print("didStartSending \(message.debugDescription)\n URL \(message.url?.absoluteString ?? "no URL")") } override func didCancelSending(_ message: MSMessage, conversation: MSConversation) { // Called when the user deletes the message without sending it. // Use this to clean up state related to the deleted message. - } + os_log("didCancelSending") + print("didCancelSending \(message.debugDescription)\n URL \(message.url?.absoluteString ?? "no URL")") +} override func willTransition(to presentationStyle: MSMessagesAppPresentationStyle) { // Called before the extension transitions to a new presentation style. @@ -64,4 +132,68 @@ class MessagesViewController: MSMessagesAppViewController { // Use this method to finalize any behaviors associated with the change in presentation style. } + func buttonMatching(mood:Mood) -> UIButton { + switch mood { + case .happy: return happyBtn + case .quizzical: return quizzicalBtn + case .distraught: return distraughtBtn + default: return angryBtn + } + } + + func send(mood:Mood, label:String) { + guard let conversation = activeConversation else { fatalError("Expected a conversation") } + guard var urlComps = URLComponents(string:"data:,") else { + fatalError("Invalid base URL") + } + var qi = [ + URLQueryItem(name: Mood.moodKey, value:mood.rawValue) + ] + if receivedMood != nil { + qi.append(URLQueryItem(name:responseKey, value:receivedMood!.rawValue)) + } + qi.append(URLQueryItem(name:senderTimestampKey, value:Date().description)) + urlComps.queryItems = qi + let layout = MSMessageTemplateLayout() + layout.caption = label + let session = conversation.selectedMessage?.session + let message = MSMessage(session: session ?? MSSession()) + message.layout = layout + message.url = urlComps.url + conversation.insert(message) { (error) in + if let error = error { + os_log("Error with MSConversation.insert(message)") + print(error) + } + } + dismiss() + } + + + // MARK: - Buttons + + @IBAction public func onHappy(_ sender: UIButton) { + send(mood:.happy, label:sender.currentTitle!) + } + + @IBAction public func onQuizzical(_ sender: UIButton) { + send(mood:.quizzical, label:sender.currentTitle!) + } + + @IBAction public func onDistraught(_ sender: UIButton) { + send(mood:.distraught, label:sender.currentTitle!) + } + + @IBAction public func onAngry(_ sender: UIButton) { + send(mood:.angry, label:sender.currentTitle!) + } + + // WARNING due to Simulator/XCode bugs, trying to invoke this in the simulator will cause a SIGKILL as it exits the iMessage context + // that was the case in XCode 10 at least + @IBAction func onLaunchApp(_ sender: Any) { + guard let url: URL = URL(string: "imUrlDataAppSUI://?arbitraryParam=nothingSpecial") else { return } + self.extensionContext?.open(url, completionHandler: { (success: Bool) in + // nothing, we invoked the main app! + }) + } } diff --git a/imUrlDataAppSUI/imUrlDataAppSUI IM/UIViewController+EmbedSwiftUI.swift b/imUrlDataAppSUI/imUrlDataAppSUI IM/UIViewController+EmbedSwiftUI.swift index 262ea48..1aab0dd 100644 --- a/imUrlDataAppSUI/imUrlDataAppSUI IM/UIViewController+EmbedSwiftUI.swift +++ b/imUrlDataAppSUI/imUrlDataAppSUI IM/UIViewController+EmbedSwiftUI.swift @@ -10,7 +10,7 @@ extension UIViewController { /// - Parameters: /// - swiftUIView: The SwiftUI `View` to add as a child. /// - view: The `UIView` instance to which the view should be added. - @available(iOS 14, *) // setting 14 as a bettr bare minimum + @available(iOS 15, *) // setting 15 as a bettr bare minimum and that's what Touchgram v1.4.0 uses func addSubSwiftUIView(_ swiftUIView: Content, to view: UIView) where Content : View { let hostingController = UIHostingController(rootView: swiftUIView) diff --git a/imUrlDataAppSUI/imUrlDataAppSUI.xcodeproj/project.pbxproj b/imUrlDataAppSUI/imUrlDataAppSUI.xcodeproj/project.pbxproj index 503dfeb..08cce38 100644 --- a/imUrlDataAppSUI/imUrlDataAppSUI.xcodeproj/project.pbxproj +++ b/imUrlDataAppSUI/imUrlDataAppSUI.xcodeproj/project.pbxproj @@ -7,13 +7,18 @@ objects = { /* Begin PBXBuildFile section */ + 42089156289F841F0036C790 /* SharedData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42089154289F841F0036C790 /* SharedData.swift */; }; + 42089157289F841F0036C790 /* SharedData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42089154289F841F0036C790 /* SharedData.swift */; }; + 42089158289F841F0036C790 /* Mood.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42089155289F841F0036C790 /* Mood.swift */; }; + 42089159289F841F0036C790 /* Mood.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42089155289F841F0036C790 /* Mood.swift */; }; + 4208915B289F97E90036C790 /* MessageComposingHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4208915A289F97E90036C790 /* MessageComposingHelper.swift */; }; + 4208915D28A01BE40036C790 /* MessagePickerHost.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4208915C28A01BE40036C790 /* MessagePickerHost.swift */; }; 42D77E6B2850EBC400B3985F /* Messages.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 42D77E6A2850EBC400B3985F /* Messages.framework */; }; 42D77E6E2850EBC400B3985F /* MessagesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42D77E6D2850EBC400B3985F /* MessagesViewController.swift */; }; 42D77E712850EBC400B3985F /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 42D77E6F2850EBC400B3985F /* MainInterface.storyboard */; }; 42D77E732850EBC500B3985F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 42D77E722850EBC500B3985F /* Assets.xcassets */; }; 42D77E772850EBC500B3985F /* imUrlDataAppSUI IM.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 42D77E682850EBC400B3985F /* imUrlDataAppSUI IM.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 42D77E7D28521D1900B3985F /* UIViewController+EmbedSwiftUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42D77E7C28521D1900B3985F /* UIViewController+EmbedSwiftUI.swift */; }; - 42D77E7E28522B7000B3985F /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42E231C12714963F0041B8CA /* ContentView.swift */; }; 42E231C02714963F0041B8CA /* imUrlDataAppSUIApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42E231BF2714963F0041B8CA /* imUrlDataAppSUIApp.swift */; }; 42E231C22714963F0041B8CA /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42E231C12714963F0041B8CA /* ContentView.swift */; }; 42E231C4271496410041B8CA /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 42E231C3271496410041B8CA /* Assets.xcassets */; }; @@ -45,6 +50,10 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 42089154289F841F0036C790 /* SharedData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SharedData.swift; path = Common/SharedData.swift; sourceTree = ""; }; + 42089155289F841F0036C790 /* Mood.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Mood.swift; path = Common/Mood.swift; sourceTree = ""; }; + 4208915A289F97E90036C790 /* MessageComposingHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageComposingHelper.swift; sourceTree = ""; }; + 4208915C28A01BE40036C790 /* MessagePickerHost.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessagePickerHost.swift; sourceTree = ""; }; 42D77E682850EBC400B3985F /* imUrlDataAppSUI IM.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "imUrlDataAppSUI IM.appex"; sourceTree = BUILT_PRODUCTS_DIR; }; 42D77E6A2850EBC400B3985F /* Messages.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Messages.framework; path = System/Library/Frameworks/Messages.framework; sourceTree = SDKROOT; }; 42D77E6D2850EBC400B3985F /* MessagesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessagesViewController.swift; sourceTree = ""; }; @@ -78,6 +87,15 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 42768103289C5A8E008FDF6C /* Common */ = { + isa = PBXGroup; + children = ( + 42089155289F841F0036C790 /* Mood.swift */, + 42089154289F841F0036C790 /* SharedData.swift */, + ); + name = Common; + sourceTree = ""; + }; 42D77E692850EBC400B3985F /* Frameworks */ = { isa = PBXGroup; children = ( @@ -101,6 +119,7 @@ 42E231B32714963F0041B8CA = { isa = PBXGroup; children = ( + 42768103289C5A8E008FDF6C /* Common */, 42E231BE2714963F0041B8CA /* imUrlDataAppSUI */, 42D77E6C2850EBC400B3985F /* imUrlDataAppSUI IM */, 42D77E692850EBC400B3985F /* Frameworks */, @@ -120,10 +139,12 @@ 42E231BE2714963F0041B8CA /* imUrlDataAppSUI */ = { isa = PBXGroup; children = ( + 4208915A289F97E90036C790 /* MessageComposingHelper.swift */, 42E231BF2714963F0041B8CA /* imUrlDataAppSUIApp.swift */, 42E231C12714963F0041B8CA /* ContentView.swift */, 42E231C3271496410041B8CA /* Assets.xcassets */, 42E231C5271496410041B8CA /* Preview Content */, + 4208915C28A01BE40036C790 /* MessagePickerHost.swift */, ); path = imUrlDataAppSUI; sourceTree = ""; @@ -238,9 +259,10 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 42089159289F841F0036C790 /* Mood.swift in Sources */, + 42089157289F841F0036C790 /* SharedData.swift in Sources */, 42D77E6E2850EBC400B3985F /* MessagesViewController.swift in Sources */, 42D77E7D28521D1900B3985F /* UIViewController+EmbedSwiftUI.swift in Sources */, - 42D77E7E28522B7000B3985F /* ContentView.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -248,6 +270,10 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 4208915B289F97E90036C790 /* MessageComposingHelper.swift in Sources */, + 4208915D28A01BE40036C790 /* MessagePickerHost.swift in Sources */, + 42089158289F841F0036C790 /* Mood.swift in Sources */, + 42089156289F841F0036C790 /* SharedData.swift in Sources */, 42E231C22714963F0041B8CA /* ContentView.swift in Sources */, 42E231C02714963F0041B8CA /* imUrlDataAppSUIApp.swift in Sources */, ); @@ -286,7 +312,7 @@ INFOPLIST_FILE = "imUrlDataAppSUI IM/Info.plist"; INFOPLIST_KEY_CFBundleDisplayName = "imUrlDataAppSUI IM"; INFOPLIST_KEY_NSHumanReadableCopyright = ""; - IPHONEOS_DEPLOYMENT_TARGET = 15.5; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -313,7 +339,7 @@ INFOPLIST_FILE = "imUrlDataAppSUI IM/Info.plist"; INFOPLIST_KEY_CFBundleDisplayName = "imUrlDataAppSUI IM"; INFOPLIST_KEY_NSHumanReadableCopyright = ""; - IPHONEOS_DEPLOYMENT_TARGET = 15.5; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -462,7 +488,7 @@ INFOPLIST_KEY_UILaunchScreen_Generation = YES; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - IPHONEOS_DEPLOYMENT_TARGET = 14.0; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -493,7 +519,7 @@ INFOPLIST_KEY_UILaunchScreen_Generation = YES; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - IPHONEOS_DEPLOYMENT_TARGET = 14.0; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", diff --git a/imUrlDataAppSUI/imUrlDataAppSUI/ContentView.swift b/imUrlDataAppSUI/imUrlDataAppSUI/ContentView.swift index 6901dd1..67896d1 100644 --- a/imUrlDataAppSUI/imUrlDataAppSUI/ContentView.swift +++ b/imUrlDataAppSUI/imUrlDataAppSUI/ContentView.swift @@ -14,10 +14,15 @@ struct ContentView: View { @State var quizzicalAllowed = true @State var distraughtAllowed = true @State var angryAllowed = true + @State var showingMessageSender = false var happyOpacity: Double {happyAllowed ? 1.0 : 0.3} var quizzicalOpacity: Double {quizzicalAllowed ? 1.0 : 0.3} var distraughtOpacity: Double {distraughtAllowed ? 1.0 : 0.3} var angryOpacity: Double {angryAllowed ? 1.0 : 0.3} + + var messager = MessageComposingHelper() + var lastTappedMood = Mood.happy + var body: some View { VStack(alignment: .center) { Text("Choose moods allowed to send") @@ -40,7 +45,10 @@ struct ContentView: View { } } .font(Font.system(size: 80.0)) - Button("Send Message from App", action: {}) + Button("Send Message from App", action: {showingMessageSender = true}) + } + .sheet(isPresented: $showingMessageSender) { + } } } diff --git a/imUrlDataAppSUI/imUrlDataAppSUI/MessageComposingHelper.swift b/imUrlDataAppSUI/imUrlDataAppSUI/MessageComposingHelper.swift new file mode 100644 index 0000000..3b532d2 --- /dev/null +++ b/imUrlDataAppSUI/imUrlDataAppSUI/MessageComposingHelper.swift @@ -0,0 +1,31 @@ +// +// MessageComposerInApp.swift +// imUrlDataApp +// +// Created by AndyDent on 5/8/2022. +// Copyright © 2022 Touchgram Pty Ltd. All rights reserved. +// + +import Foundation +import MessageUI +import Messages + +/// helper class originally used to provide a static method to let you send the message from teh app +class MessageComposingHelper: NSObject { + + func canSendText() -> Bool { + MFMessageComposeViewController.canSendText() + } + /* + func displayMessageInterface(onVC vc: UIViewController, mood:Mood) { + + // Present the view controller modally. + if MFMessageComposeViewController.canSendText() { + vc.present(composeVC, animated: true, completion: nil) + } else { + print("Can't send messages.") + } + } +*/ + //MARK - conform to MFMessageComposeViewControllerDelegate +} diff --git a/imUrlDataAppSUI/imUrlDataAppSUI/MessagePickerHost.swift b/imUrlDataAppSUI/imUrlDataAppSUI/MessagePickerHost.swift new file mode 100644 index 0000000..b3a9101 --- /dev/null +++ b/imUrlDataAppSUI/imUrlDataAppSUI/MessagePickerHost.swift @@ -0,0 +1,69 @@ +// +// MessagePickerHost.swift +// imUrlDataAppSUI +// +// Created by AndyDent on 8/8/2022. +// Aided by https://www.hackingwithswift.com/books/ios-swiftui/wrapping-a-uiviewcontroller-in-a-swiftui-view + +import Foundation +import SwiftUI +import MessageUI +import Messages + +struct MessagePickerHost: UIViewControllerRepresentable { + + typealias UIViewControllerType = MFMessageComposeViewController + + class Coordinator: NSObject, MFMessageComposeViewControllerDelegate { + func messageComposeViewController(_ controller: MFMessageComposeViewController, + didFinishWith result: MessageComposeResult) { + // Check the result or perform other tasks. + let msgStr = result == .cancelled ? + "Cancelled" : result == .failed ? + "Failed" : + "Sent" + print(msgStr) + // Dismiss the message compose view controller. + controller.dismiss(animated: true, completion: nil) + } + } + + func makeUIViewController(context: Context) -> MFMessageComposeViewController { + let composeVC = MFMessageComposeViewController() + composeVC.messageComposeDelegate = context.coordinator + + // Configure the fields of the interface. + composeVC.recipients = ["123456"] + composeVC.body = "Sending a custom message" + // + /* + According to + https://developer.apple.com/documentation/messageui/mfmessagecomposeviewcontroller/2213331-message + If your app has an iMessage app extension, you can display your iMessage app within the message compose view, just as you would in the Messages app. To display your iMessage app, create and assign an MSMessage object to this property. + + By default, this property is set to nil. + */ + if #available(iOS 10, *) { // necessary if clause to make XCode happy to use composeVC.message + let message = MSMessage(session: MSSession()) + // fake building a smiley using hardcoded stuff to match MessagesViewController.send + guard var urlComps = URLComponents(string:"data:,") else { + fatalError("Invalid base URL") + } + urlComps.queryItems = [URLQueryItem(name: Mood.moodKey, value:Mood.happy.rawValue)] + message.url = urlComps.url + composeVC.message = message + } + return composeVC + } + + func updateUIViewController(_ uiViewController: MFMessageComposeViewController, context: Context) { + // is not used + } + + func makeCoordinator() -> Coordinator { + Coordinator() + } + + + +} From f61ef1f1c435aabe5699437748d6641f49e3dafb Mon Sep 17 00:00:00 2001 From: Andy Dent Date: Mon, 8 Aug 2022 12:43:10 +0800 Subject: [PATCH 7/7] readme update --- imStickered/README.md | 4 +- .../Base.lproj/MainInterface.storyboard | 148 ++++++++++-------- 2 files changed, 85 insertions(+), 67 deletions(-) diff --git a/imStickered/README.md b/imStickered/README.md index 663c805..129f6f4 100644 --- a/imStickered/README.md +++ b/imStickered/README.md @@ -3,7 +3,9 @@ Copied from webFromIM & reworked. See the overall rationale in the [readme in the parent folder](../README.md). -This sample explores using [Snap's StickerKit][SK] as it allows for using Stickers without login. There will also be a sample exploring [LoginKit] and BitMoji. This simpler one seeks to show the ability to pull in stickers only in the iMessage app extension without worrying about deeplinking callback issues. +This sample explores using [Snap's StickerKit][SK] as it allows for using Stickers without login. There may also be a sample exploring [LoginKit] and BitMoji. This simpler one seeks to show the ability to pull in stickers only in the iMessage app extension without worrying about deeplinking callback issues. + +Similar Note that this is a sample app under the MIT License (as per `../LICENSE`) so feel free to copy to build your own apps on top of the code here. I may have taken minor shortcuts or engineering approaches inadequate for production apps. diff --git a/imStickered/imStickered MessagesExtension/Base.lproj/MainInterface.storyboard b/imStickered/imStickered MessagesExtension/Base.lproj/MainInterface.storyboard index f14954b..9029830 100644 --- a/imStickered/imStickered MessagesExtension/Base.lproj/MainInterface.storyboard +++ b/imStickered/imStickered MessagesExtension/Base.lproj/MainInterface.storyboard @@ -1,10 +1,12 @@ - + - + + + @@ -16,85 +18,99 @@ - - - + + - - - - - - + + + + - - - - + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - + - - - - - - - - - + + + + + + + + + + + + + +