How to Add SceneDelegate to an Existing Storyboard Project in XCode

I have an old project previously built on IOS12 and now trying to add new features on IOS 13. One of the things I’m trying to wrap my head around before I start adding new functionalities to my app is the introduction of UIWindowScene and the support for multiple window for iOS and iPadOS.

What is UIWindowScene? As per the documentation – this is an object that manages one instance of your app’sUI, including one ore more windows that is displayed from that scene.

Before iOS 13, the AppDelegate is the starting point of an App. It was in charge of setup, state handling, and other logic as such as below:

  • setup first view controller – root view controller
  • configure app settings and startup components
    • logging
    • cloud services
    • persistent container
  • register push notification handlers and respond to push notifications sent to the app
  • respond to app lifecycle events
    • entering background
    • resuming app
    • existing app

My didFinishLaunchingWithOptions method implemented on the AppDelegate looks like this.

self.window object on the snippet above is a property of the AppDelegate and is the only one window my app uses. It manages the app’s UI, dispatch events to views and the main backdrop to display the app’s content.

On iOS 13, the AppDelegate is responsible for the initial setup only. The SceneDelegate takes over some of it’s roles, one of which is the window and is replaced by a scene. An app can now have more than one instance of the window/scene. So now you can have an app with multiple scenes.

For example for a word processor app could have every text document as a scene.

Now that the concepts has been discussed and hopefully clear. The following files on my existing project needs to be modified.

  • Adding a SceneDelegate class
    • Either create a new class SceneDelegate and paste the boiler plate code or create a new project on Xcode and copy the SceneDelegate from that project.
  • Updating the AppDelegate class
    • Existing implementation methods can be removed and add the two scene related implementation methods
  • Updating the info.plist file
    • for this one click on + icon the last item of the info.plist then choose Application Scene Manifest
    • Expand that and replicate the options as per the screenshot.

Fingers crossed all changes right, build your app. Get that sweet Build Succeeded message. Your app is now running on its own instance of the window.

How To Set Up CICD On Bitbucket Pipelines With Salesforce DX And Delta Deployment

Learn how to setup set up CICD delta deployment with Salesforce DX. Tips and tricks for authorisation , setting up node and the basic git commands.

I’m revamping our CICD process with Salesforce DX and Bitbucket Pipeline with the following initial setup which will only a delta deployment

Authentication method – authorize an org and grab the sfdxurl to be stored as repository variable in Bitbucket

sfdx force:auth:web:login 
sfdx force:org:display --verbose

There would be two token types

force://<refreshToken>@<instanceUrl> 
or 
force://<clientId>:<clientSecret>:<refreshToken>@<instanceUrl>

Copy the SFDX Auth URL which will be the second type. Create a repository variable AUTH_URL in Bitbucket and store the copied value.

Echo the AUTH_URL to a file then authenticate with with sfdxurl:store

echo $AUTH_URL >> /tmp/sfdx_auth.txt
sfdx force:auth:sfdxurl:store -f /tmp/sfdx_auth.txt -s -a dxpipeline

Grab the latest sfdx tool and install.

wget https://developer.salesforce.com/media/salesforce-cli/sfdx-linux-amd64.tar.xz 
mkdir sfdx-cli 
tar xJf sfdx-linux-amd64.tar.xz -C sfdx-cli --strip-components 1 
./sfdx-cli/install

Next, to compare delta files – there is node tool available in github that does delta comparison between hash commit or branch. Install the sfdx-git-delta app

npm install sfdx-git-delta@latest -g

Finally I incorporated these to my git workflow

On a Pull Request – I want to run a delta comparison and do an empty check only that my delta files changes are deployable and does break any unit tests.

First checkout a temporary branch from the feature branch

git checkout -b some-pr-branch

Next, run the tool to create a delta comparison from that branch to the target branch.

sgd --to some-pr-branch --from origin/staging --repo . --output .

The tool should create a package.xml/destructiveChange.xml file based on the diff on their respective directory.

Next convert the source format to mdapi so we can run a transactional deploy.

sfdx force:source:convert --manifest=package/package.xml --outputdir=convert

After conversion, do an empty check deploy and run the unit test

sfdx force:mdapi:deploy --deploydir=convert -c -l RunLocalTests -w 30

Below is the complete Pull Request script.

image: atlassian/default-image:2

pipelines:
  pull-requests:
    'feature/*': # Pull request from feature branch to Staging
      - step:
          name: "Staging Pull Request Validate Package"
          script:
            - echo "QA Pull Request Validation"
            - wget https://developer.salesforce.com/media/salesforce-cli/sfdx-linux-amd64.tar.xz
            - mkdir sfdx-cli
            - tar xJf sfdx-linux-amd64.tar.xz -C sfdx-cli --strip-components 1
            - ./sfdx-cli/install
            - echo $AUTH_URL >> /tmp/sfdx_auth.txt
            - sfdx force:auth:sfdxurl:store -f /tmp/sfdx_auth.txt -s -a dxpipeline
            - npm install sfdx-git-delta@latest -g
            - git checkout -b some-pr-branch          
            - git --no-pager diff --name-status some-pr-branch  origin/staging
            - sgd --to some-pr-branch  --from origin/staging --repo . --output .
            - echo "--- package.xml generated with added and modified metadata ---"
            - cat package/package.xml
            - sfdx force:source:convert --manifest=package/package.xml --outputdir=convert 
            - echo "---- Validating delta package  ----"
            - sfdx force:mdapi:deploy --deploydir=convert -c -l RunLocalTests -w 30

On Push to the branch – I ran similar steps with the only exception that I compare the current branch to the staging branch and not do an empty check or run the test classes as I already ran them.

Below is the complete Push script.

image: atlassian/default-image:2

pipelines:
  pushs:
    staging: 
      - step:
          name: "Deploy to Staging"
          script:
            - echo "Deploy to Staging"
            - wget https://developer.salesforce.com/media/salesforce-cli/sfdx-linux-amd64.tar.xz
            - mkdir sfdx-cli
            - tar xJf sfdx-linux-amd64.tar.xz -C sfdx-cli --strip-components 1
            - ./sfdx-cli/install
            - echo $AUTH_URL >> /tmp/sfdx_auth.txt
            - sfdx force:auth:sfdxurl:store -f /tmp/sfdx_auth.txt -s -a dxpipeline
            - npm install sfdx-git-delta@latest -g
            - git checkout -b dev          
            - git --no-pager diff --name-status some-pr-branch  origin/staging
            - sgd --to dev  --from origin/staging --repo . --output .
            - echo "--- package.xml generated with added and modified metadata ---"
            - cat package/package.xml
            - sfdx force:source:convert --manifest=package/package.xml --outputdir=convert 
            - echo "---- Validating delta package  ----"
            - sfdx force:mdapi:deploy --deploydir=convert -w 30

Hope you find this useful. Hit me up on the comments below for any questions.

How To Use Javascript Promises with Lightning Components

Javascript Promises has been around for a while but only got the chance to use it on some of the aura component pieces I started working on.

In analogy you make a promise and either you fulfil or break your promise.

In Javascript Promises context these translate to “resolve” meaning promise is fulfilled or “reject” which means promise was broken and can be caused by an error.

A good use case for in Lightning is handling responses from asynchronous operations in which you pass a callback function. Then that callback function can make another asynchronous operation. Keep on nesting and you can easily eventually end with what they sometimes call callback hell as your code can be hard to manage.

Let’s dive into a creating Javascript Promise

I defined this as a helper method. It calls an apex method and depending on response and getState I mapped it to either resolve or reject.

Here I assigned a variable to the returned Promise in the javascript controller. The helper returns one parameter which is the response which I can access on the .then statement.

Here we called the p variable followed by a .then and the special callback function for Aura which is $A.getCallback. You can then chain another promise by calling and returning that promise. The next then can accept a parameter from the previous promise.

With Javascript Promises, this is more readable than a nested callback and easier to manage.

I hope you find this basic tip useful. Hit the comments below if you have questions.

How To Generate a Self-Signed SSL Certificate With SAN

For development and integration use cases you may need to create or renew a self-signed certificate and store the certificate to your web server host or pass the certificate to your target system to trust only connections from an app using the certificate.

If you are storing the certificate on your webserver and enabled only secured connection via HTTPS on your server.

Chrome may not recognize the SSL certificate as secure without SAN (Subject Alternative Name).

Prerequisite: You should have openssl installed on your machine. Check and download from https://www.openssl.org/source/

To create one in command do the following steps:

Create a configuration file: eg. req.cnf

[req]
distinguished_name = req_distinguished_name
x509_extensions = v3_req
prompt = no
[req_distinguished_name]
C = NZ
ST = AU
L = Auckland
O = Quonsepto
OU = MyDivision
CN = localhost
[v3_req]
keyUsage = critical, digitalSignature, keyAgreement
extendedKeyUsage = serverAuth
subjectAltName = @alt_names
[alt_names]
DNS.1 = localhost

Next from the terminal or command prompt run the following:

openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout server.key -out server.crt -config req.cnf -sha256

You can then generated certificate and it should be compatible with Chrome.

Check this video for sample installing on a locally hosted node app.

Milestone Reached! 1000 Subscribers in my Youtube Channel

Finally, thanks to viewers and subscribers to my channel. Just Another Dang How To Channel – a channel where I share how-to tech videos. A small milestone for me but super happy. It’s been a roller coaster ride reaching this milestone.

June 8, 2020

I started the channel way back Feb 2017 when Youtube didn’t have a strict Partner Program policy, you can upload videos and immediately earn from your videos. Then a few months after that, the policy changed to have at least 100,000 channel views for channel to be able to be monetized, I didnt meet the criteria and my channel got demonetized, I persevered, I kept sharing videos and eventually met the requirement.

But on Feb 2018 Youtube again changed policy which wiped out all small channels, new requirements were to have at least 1000 subscribers, and 4000 hours channel watch time for 365 days. I was gutted and lost interest. I abandoned the channel for several months but my watch time kept growing. Last year April 2019, I decided to revive it as my channel met the 4000 hours criteria. I had about 600 subs at that time. 1 year and 2 months later I finally reached 1000 subscribers.

My takeaway is to don’t give up and keep on persevering. Rome wasn’t built in a day. As of this writing, I am already now at 1004 subscribers and waiting for approval. Next goal up, get 2500 subscribers.

If you haven’t yet, please subscribe to my Youtube channel.

How To Use Map Object In Aura Lightning Component

By Salesforce documentation, you can define several collection types including a Map.

A Map collection allows you have a key/value pair where the key is unique. Declaring such is easy by adding the following

<aura:attribute type="Map" name="fooMap" />

But in your controller, if you try to do any Map functions such as keys(), set(key, value), values(), you get an error such as:

set is not a function 

or

values is not a function.

What is happening in Lightning is even if you declared it as Map it is treated as an Object. It took me a while to figure this out.

To get around this “limitation” I manually assigned a map in the controller and then I was able to do Map functions. You can do this either on init or before you use the component Map.

Hope you find this tip useful.

How To Create Symlink For Sublime Text and Visual Studio Code in a Mac

This tip is for creating symlink for your favorite text editor, it is like an alias but more powerful. This would allow you to launch the application from the command line. ( You can do the same by setting paths as an alternative to symlinks). Symlinks can only be created from the terminal.

First find out the bin executable directory for the application. Then for the location where the symlink is to be created – /usr/local/bin/

The following command allows you to create symlink for Sublime Text

sudo ln -s "/Applications/Sublime Text.app/Contents/SharedSupport/bin/subl" /usr/local/bin/subl

Then to launch it from the terminal simply type : subl

The following command allows you to create symlink for Visual Studio Code

sudo ln -s "Applications/Visual Studio Code.app/Contents/Resources/app/bin/code" /usr/local/bin/code

Then to launch it from the terminal simply type : code

How To Replace Salesforce Metadata Before Deploying using Ant Scripts

My particular use case is for Salesforce ant deployment. I wanted to replace some metadata before I deploy to the target org which will allow me to automate the process. I can fetch metadata from my sandbox org and when it gets deployed to the target org like production the values will be updated.

You should have the latest ant-salesforce.jar as a requirement. You can grab the latest ant migration tool from here – https://help.salesforce.com/articleView?id=code_tools_ant_using.htm&type=5

My sample script entails having a conditional check before doing the replace a logic with Custom Labels. This is how my build XML looks.

Additional library you would need to perform the conditional check is ant-contrib.jar file. You can grab the latest library from here – http://ant-contrib.sourceforge.net/

If you try to run the script without the library you might end with the error below.

Fix Ant Build Error: Problem: failed to create task or type if

On the build.xml simply add the reference to the library.

If everything is in place, like the properties file has the right credentials, running the following command should deploy your code and replace the values as per your ant script.

ant -Denvironment=prod -buildfile build.xml deployMetadata

Source code available here – https://github.com/olopsman/salesforce-ant

Fix Swift Framework Error: Module compiled with Swift 5.1.3 cannot be imported by the Swift 5.2.4 compiler:

Chances are you upgraded to the latest Xcode and along with it, your Swift compiler is updated as well. Then existing projects using a particular project have stopped working with similar error such as.

Module compiled with Swift 5.1.3 cannot be imported by the Swift 5.2.4 compiler:

/Users/PauloOrquilo/Documents/Development/Mobile/Training/CoreData/Sync/iOSDemo/Carthage/Build/iOS/Sync.framework/Modules/Sync.swiftmodule/x86_64.swiftmodule

Or you are checking out some hosted on code on Github using some framework, the framework might have been built using an older compiler version, if you try to run the associated .xcodeproj or .xworkspace it won’t build the project.

This is only for frameworks built using Carthage. I haven’t experienced this on CocoaPods. Make sure you have Carthage installed – check this documentation on how to install Carthage – https://github.com/Carthage/Carthage

To fix, take a backup of your project, or better yet make sure you are using version control. Delete the framework that is failing from your project.

Go to the Project directory from the Terminal and make sure the Cartfile is available and run. Choose the right platform for the app.

carthage update --platform IOS

It should create a newly .framework on the Carthage/Build folder.

On Xcode go the General Tab and under Framework, Libraries and Embedded Content. Drag and drop the files

Then finally, try running the build and your project should be working.

Hope you find this tip useful.

Troubleshooting pod install Error on CocoaPods

Going through the Getting Started guide for Firebase I encountered this error ‘JSON::ParserError – 767: unexpected token at ‘ and thought to blog and share how to fix it.

I went to the Xcode project directory in the terminal and ran

pod init

Then I defined the PodFile spec

# Uncomment the next line to define a global platform for your project
# platform :ios, '9.0'

target 'Firebase Getting Started' do
  # Comment the next line if you don't want to use dynamic frameworks
  #use_frameworks!

  # Pods for Firebase Getting Started
  pod 'Firebase/Analytics'
  # Add the pods for any other Firebase products you want to use in your app
  # For example, to use Firebase Authentication and Cloud Firestore
  pod 'Firebase/Auth'
  pod 'Firebase/Firestore'

end

But when I ran

pod install

I encountered this error

[!] Oh no, an error occurred.

Search for existing GitHub issues similar to yours:
https://github.com/CocoaPods/CocoaPods/search?q=767%3A+unexpected+token+at+%27%27&type=Issues

If none exists, create a ticket, with the template displayed above, on:
https://github.com/CocoaPods/CocoaPods/issues/new

Be sure to first read the contributing guide for details on how to properly submit a ticket:
https://github.com/CocoaPods/CocoaPods/blob/master/CONTRIBUTING.md

Don't forget to anonymize any private data!

Looking for related issues on cocoapods/cocoapods...
 - [1.9.2] Error during pod install JSON::ParserError - 767: unexpected token
   https://github.com/CocoaPods/CocoaPods/issues/9814 [open] [31 comments]
   8 hours ago

 - https://github.com/CocoaPods/CocoaPods/search?q=767%3A+unexpected+token+at+%27%27&type=Issues
   https://github.com/CocoaPods/CocoaPods/issues/9777 [closed] [3 comments]
   2 weeks ago

 - Error during pod install: Encountered an unknown error (783: unexpected token at
   https://github.com/CocoaPods/CocoaPods/issues/9672 [closed] [92 comments]
   22 hours ago

and 12 more at:
https://github.com/cocoapods/cocoapods/search?q=767%3A%20unexpected%20token%20at%20%27%27&type=Issues&utf8=?

[!] Automatically assigning platform `iOS` with version `13.5` on target `Firebase Getting Started` because no platform was specified. Please specify a platform for this target in your Podfile. See `https://guides.cocoapods.org/syntax/podfile.html#platform`.

After checking the issues. It turns out the cocoapods repo cache on my machine somehow got corrupted. To fix the error I simply deleted the cache.

sudo rm -rf ~/.cocoapods/repos

And running pod install after that worked.