DiscussionThe AnyHashable type forwards equality comparisons and hashing operations to an underlying hashable value, hiding its specific underlying type.You can store mixed-type keys in dictionaries and other collections that require Hashable conformance by wrapping mixed-type keys in AnyHashable instances
1 2 3 4 5 6 7 8 9 10
let descriptions: [AnyHashable: Any] = [ AnyHashable("😄"): "emoji", AnyHashable(42): "an Int", AnyHashable(Int8(43)): "an Int8", AnyHashable(Set(["a", "b"])): "a set of strings" ] print(descriptions[AnyHashable(42)]!) // prints "an Int" print(descriptions[AnyHashable(43)]) // prints "nil" print(descriptions[AnyHashable(Int8(43))]!) // prints "an Int8" print(descriptions[AnyHashable(Set(["a", "b"]))]!) // prints "a set of strings"
We don’t necessarily need to map from [AnyHashable: Any] to [String: Any], we can just access via string key
/// The content of the scroll view. publicvar content: Content
}
1 2 3 4 5 6 7 8 9 10
extensionGroup : ViewwhereContent : View{
/// The type of view representing the body of this view. /// /// When you create a custom view, Swift infers this type from your /// implementation of the required `body` property. publictypealiasBody = Never
Use ObservableObject and onReceive to receive event. URLSession.dataTask reports in background queue, so need to .receive(on: RunLoop.main) to receive events on main queue.
For better dependency injection, need to use ImageLoader from Environment
There should be a way to propagate event from Publisher to another Publisher, for now we use sink
If this property is nil, all valid source files in the target’s path will be included and specified paths are relative to the target path.
A path can be a path to a directory or an individual source file. In case of a directory, the Swift Package Manager searches for valid source files recursively inside it.
A string containing the localized description of the error. The object in the user info dictionary for the key NSLocalizedDescriptionKey. If the user info dictionary doesn’t contain a value for NSLocalizedDescriptionKey, a default string is constructed from the domain and code.
When you adopt App Sandbox, your application has access to the following locations:
The app container directory. Upon first launch, the operating system creates a special directory for use by your app—and only by your app—called a container. Each user on a system gets an individual container for your app, within their home directory; your app has unfettered read/write access to the container for the user who ran it.
module.exports = asyncfunction (params) { // Only notarize the app on Mac OS only. if (process.platform !== 'darwin') { return; } console.log('afterSign hook triggered', params);
// Same appId in electron-builder. let appId = 'com.onmyway133.IconGenerator'
let appPath = path.join(params.appOutDir, `${params.packager.appInfo.productFilename}.app`); if (!fs.existsSync(appPath)) { thrownewError(`Cannot find application at: ${appPath}`); }
console.log(`Notarizing ${appId} found at ${appPath}`);
export appleId=onmyway133@gmail.com export appleIdPassword=1234-abcd-efgh-7890 npm run dist
Check
1
spctl --assess --verbose Icon\ Generator.app
Troubleshooting
babel
Since electron-builder create dist folder for distribution, for example dist/mac/Icon Generator, I’ve renamed babel generated code to babel directory
babel 6 regeneratorRuntime is not defined
It is because of afterSignHook. Ignore in .babelrc not work
Indicates whether the menu automatically enables and disables its menu items.
This property contains a Boolean value, indicating whether the menu automatically enables and disables its menu items. If set to true, menu items of the menu are automatically enabled and disabled according to rules computed by the NSMenuValidation informal protocol. By default, NSMenu objects autoenable their menu items.
Returns an Observable that emits the first and the latest item emitted by the source Observable during sequential time windows of a specified duration. This operator makes sure that no two elements are emitted in less then dueTime.
In a time window, only the first item gets emitted.
💡 In other words, in a time window, take first and discard following.
For example, when failure, we show error message but don’t want to show error messages consecutively. We can use throttle to discard consecutive errors.
Ignores elements from an observable sequence which are followed by another element within a specified relative time duration, using the specified scheduler to run throttling timers.
One confusing point here that people often do not realize is that even though the custom token itself expires after one hour, a modern client SDK authenticated with that custom token will stay authenticated beyond that hour! What happens under the hood is that the custom token is sent to the Firebase Auth service in exchange for an ID token and refresh token pair which are used to keep the client SDK authenticated
As with custom tokens, ID tokens are short-lived JWTs, lasting for just one hour. In order to allow end users to stay logged in for more than one hour, the modern SDKs transparently refresh a user’s ID token on your behalf using a refresh token
If your app includes a custom backend server, ID tokens can and should be used to communicate securely with it. Instead of sending requests with a user’s raw uid which can be easily spoofed by a malicious client, send the user’s ID token which can be verified via a Firebase Admin SDK
When a user or device successfully signs in, Firebase creates a corresponding ID token that uniquely identifies them and grants them access to several resources, such as Firebase Realtime Database and Cloud Storage. You can re-use that ID token to identify the user or device on your custom backend server. To retrieve the ID token from the client, make sure the user is signed in and then get the ID token from the signed-in user: