Swift

Swift

Swift is a general-purpose, open-source programming language developed by Apple Inc. for their platforms and Linux.
Jamel  O'Reilly

Jamel O'Reilly

1656888360

SugarRecord: CoreData/Realm sweet wrapper written in Swift

What is SugarRecord?

SugarRecord is a persistence wrapper designed to make working with persistence solutions like CoreData in a much easier way. Thanks to SugarRecord you'll be able to use CoreData with just a few lines of code: Just choose your stack and start playing with your data.

The library is maintained by @carambalabs. You can reach me at pepibumur@gmail.com for help or whatever you need to commend about the library.

Features

  • Swift 3.0 compatible (Xcode 8.0).
  • Protocols based design.
  • For beginners and advanced users
  • Fully customizable. Build your own stack!
  • Friendly syntax (fluent)
  • Away from Singleton patterns! No shared states :tada:
  • Compatible with OSX/iOS/watchOS/tvOS
  • Fully tested (thanks Nimble and Quick)
  • Actively supported

Setup

CocoaPods

  1. Install CocoaPods. You can do it with gem install cocoapods
  2. Edit your Podfile file and add the following line pod 'SugarRecord'
  3. Update your pods with the command pod install
  4. Open the project from the generated workspace (.xcworkspace file).

Note: You can also test the last commits by specifying it directly in the Podfile line

Available specs Choose the right one depending ton the configuration you need for you app.

pod "SugarRecord/CoreData"
pod "SugarRecord/CoreData+iCloud"

Carthage

  1. Install Carthage. You can do it with brew install carthage.
  2. Edit your Cartfile file and add the following line `github "carambalabs/sugarrecord".
  3. Execute carthage update
  4. Add the frameworks to your project as explained on the Carthage repository.

Reference

You can check generated SugarRecord documentation here generated automatically with CocoaDocs

How to use

Creating your Storage

A storage represents your database. The first step to start using SugarRecord is initializing the storage. SugarRecord provides a default storages, CoreDataDefaultStorage.

// Initializing CoreDataDefaultStorage
func coreDataStorage() -> CoreDataDefaultStorage {
    let store = CoreDataStore.named("db")
    let bundle = Bundle(for: self.classForCoder)
    let model = CoreDataObjectModel.merged([bundle])
    let defaultStorage = try! CoreDataDefaultStorage(store: store, model: model)
    return defaultStorage
}

Creating an iCloud Storage

SugarRecord supports the integration of CoreData with iCloud. It's very easy to setup since it's implemented in its own storage that you can use from your app, CoreDataiCloudStorage:

// Initializes the CoreDataiCloudStorage
func icloudStorage() -> CoreDataiCloudStorage {
    let bundle = Bundle(for: self.classForCoder)
    let model = CoreDataObjectModel.merged([bundle])
    let icloudConfig = CoreDataiCloudConfig(ubiquitousContentName: "MyDb", ubiquitousContentURL: "Path/", ubiquitousContainerIdentifier: "com.company.MyApp.anothercontainer")
    let icloudStorage = try! CoreDataiCloudStorage(model: model, iCloud: icloudConfig)
    return icloudStorage
}

Contexts

Storages offer multiple kind of contexts that are the entry points to the database. For curious developers, in case of CoreData a context is a wrapper around NSManagedObjectContext. The available contexts are:

  • MainContext: Use it for main thread operations, for example fetches whose data will be presented in the UI.
  • SaveContext: Use this context for background operations. The context is initialized when the storage instance is created. That context is used for storage operations.
  • MemoryContext: Use this context when you want to do some tests and you don't want your changes to be persisted.

Fetching data

let pedros: [Person] = try! db.fetch(FetchRequest<Person>().filtered(with: "name", equalTo: "Pedro"))
let tasks: [Task] = try! db.fetch(FetchRequest<Task>())
let citiesByName: [City] = try! db.fetch(FetchRequest<City>().sorted(with: "name", ascending: true))
let predicate: NSPredicate = NSPredicate(format: "id == %@", "AAAA")
let john: User? = try! db.fetch(FetchRequest<User>().filtered(with: predicate)).first

Remove/Insert/Update operations

Although Contexts offer insertion and deletion methods that you can use it directly SugarRecords aims at using the operation method method provided by the storage for operations that imply modifications of the database models:

  • Context: You can use it for fetching, inserting, deleting. Whatever you need to do with your data.
  • Save: All the changes you apply to that context are in a memory state unless you call the save() method. That method will persist the changes to your store and propagate them across all the available contexts.
do {
  db.operation { (context, save) throws in
    // Do your operations here
    try save()
  }
} catch {
  // There was an error in the operation
}

New model

You can use the context new() method to initialize a model without inserting it in the context:

do {
  db.operation { (context, save) throws in
    let newTask: Track = try context.new()
    newTask.name = "Make CoreData easier!"
    try context.insert(newTask)
    try save()
  }
} catch {
  // There was an error in the operation
}

In order to insert the model into the context you use the insert() method.

Creating a model

You can use the create() for initializing and inserting in the context in the same operation:

do {
  db.operation { (context, save) throws -> Void in
    let newTask: Track = try! context.create()
    newTask.name = "Make CoreData easier!"
    save()
  }
}
catch {
  // There was an error in the operation
}

Delete a model

In a similar way you can use the remove() method from the context passing the objects you want to remove from the database:

do {
  db.operation { (context, save) throws in
    let john: User? = try context.request(User.self).filteredWith("id", equalTo: "1234").fetch().first
    if let john = john {
      try context.remove([john])
      try save()
    }
  }
} catch {
  // There was an error in the operation
}


> This is the first approach of SugarRecord for the interface. We'll improve it with the feedback you can report and according to the use of the framework. Do not hesitate to reach us with your proposals. Everything that has to be with making the use of CoreData easier, funnier, and enjoyable is welcome! :tada:

RequestObservable

SugarRecord provides a component, RequestObservable that allows observing changes in the DataBase. It uses NSFetchedResultsController under the hood.

Observing

class Presenter {
  var observable: RequestObservable<Track>!

  func setup() {
      let request: FetchRequest<Track> = FetchRequest<Track>().filtered(with: "artist", equalTo: "pedro")
      self.observable = storage.instance.observable(request)
      self.observable.observe { changes in
        case .Initial(let objects):
          print("\(objects.count) objects in the database")
        case .Update(let deletions, let insertions, let modifications):
          print("\(deletions.count) deleted | \(insertions.count) inserted | \(modifications.count) modified")
        case .Error(let error):
          print("Something went wrong")
      }
  }
}

Retain: RequestObservable must be retained during the observation lifecycle. When the RequestObservable instance gets released from memory it stops observing changes from your storage.

NOTE: This was renamed from Observable -> RequestObservable so we are no longer stomping on the RxSwift Observable namespace.

:warning: RequestObservable is only available for CoreData + OSX since MacOS 10.12

Resources

Contribute

Contributions are welcome :metal: We encourage developers like you to help us improve the projects we've shared with the community. Please see the Contributing Guide and the Code of Conduct.

License

The MIT License (MIT)

Copyright (c) 2017 Caramba

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Author: modo-studio
Source Code: https://github.com/modo-studio/SugarRecord
License: MIT license

#ios #swift 

SugarRecord: CoreData/Realm sweet wrapper written in Swift
 iOS App Dev

iOS App Dev

1656881100

WWDCast | IOS Application to Watch WWDC Videos and Sessions

WWDCast

The unofficial WWDC iOS application to watch WWDC videos and sessions on your ChromeCast.

If you would like to test the latest changes, you can join the TestFlight beta by sending your e-mail address to @sgl0v.

Features

You can watch WWDC videos on your ChromeCast. Just select a video, click the play and choose your device from the list. The app becomes the remote control to play, pause, seek, rewind, stop, and otherwise control the media.

screen1 screen2 screen3  

Dependencies

  • Xcode 9.3 or later
  • iOS 10 or later
  • Carthage (brew install carthage)
  • Swiftlint (brew install swiftlint)

Building the app

Just run these instructions:

git clone https://github.com/sgl0v/WWDCast.git
cd WWDCast
carthage update --platform iOS

Eventually you should open the WWDCast.xcworkspace and build/run WWDCast.

Other projects

WWDC app for iOS. Apple's official WWDC app for iOS where you can see the schedule, maps, videos and news.

WWDC app for macOS. The unofficial WWDC app for macOS.

Parties for WWDC. An aggregator of developer parties happening in San Jose mostly exclusively for attendees of WWDC.

ASCIIwwdc. Searchable full-text transcripts of WWDC sessions.

Download Details:
Author: sgl0v
Source Code: https://github.com/sgl0v/WWDCast
License: MIT license

#swift #ios

WWDCast | IOS Application to Watch WWDC Videos and Sessions
Jamel  O'Reilly

Jamel O'Reilly

1656877440

SwiftyUserDefaults: Modern Swift API for NSUserDefaults

SwiftyUserDefaults

Modern Swift API for NSUserDefaults

SwiftyUserDefaults makes user defaults enjoyable to use by combining expressive Swifty API with the benefits of static typing. Define your keys in one place, use value types easily, and get extra safety and convenient compile-time checks for free.

Previous versions' documentation: Version 4.0.0, Version 3.0.1
Migration guides: from 4.x to 5.x, from 4.0.0-alpha.1 to 4.0.0-alpha.3, from 3.x to 4.x

Version 5.0.0

Features

There's only one step to start using SwiftyUserDefaults:

Define your keys!

extension DefaultsKeys {
    var username: DefaultsKey<String?> { .init("username") }
    var launchCount: DefaultsKey<Int> { .init("launchCount", defaultValue: 0) }
}

And just use it ;-)

// Get and set user defaults easily
let username = Defaults[\.username]
Defaults[\.hotkeyEnabled] = true

// Modify value types in place
Defaults[\.launchCount] += 1
Defaults[\.volume] -= 0.1
Defaults[\.strings] += "… can easily be extended!"

// Use and modify typed arrays
Defaults[\.libraries].append("SwiftyUserDefaults")
Defaults[\.libraries][0] += " 2.0"

// Easily work with custom serialized types
Defaults[\.color] = NSColor.white
Defaults[\.color]?.whiteComponent // => 1.0

If you use Swift 5.1 - good news! You can also use keyPath dynamicMemberLookup:

Defaults.color = NSColor.white

See more at the KeyPath dynamicMemberLookup section.

Usage

Define your keys

To get the most out of SwiftyUserDefaults, define your user defaults keys ahead of time:

let colorKey = DefaultsKey<String>("color", defaultValue: "")

Just create a DefaultsKey object, put the type of the value you want to store in angle brackets, the key name in parentheses, and you're good to go. If you want to have a non-optional value, just provide a defaultValue in the key (look at the example above).

You can now use the Defaults shortcut to access those values:

Defaults[key: colorKey] = "red"
Defaults[key: colorKey] // => "red", typed as String

The compiler won't let you set a wrong value type, and fetching conveniently returns String.

Take shortcuts

For extra convenience, define your keys by extending magic DefaultsKeys class and adding static properties:

extension DefaultsKeys {
    var username: DefaultsKey<String?> { .init("username") }
    var launchCount: DefaultsKey<Int> { .init("launchCount", defaultValue: 0) }
}

And use the shortcut dot syntax:

Defaults[\.username] = "joe"
Defaults[\.launchCount] += 1

Supported types

SwiftyUserDefaults supports all of the standard NSUserDefaults types, like strings, numbers, booleans, arrays and dictionaries.

Here's a full table of built-in single value defaults:

Single valueArray
String[String]
Int[Int]
Double[Double]
Bool[Bool]
Data[Data]
Date[Date]
URL[URL]
[String: Any][[String: Any]]

But that's not all!

Codable

Since version 4, SwiftyUserDefaults support Codable! Just conform to DefaultsSerializable in your type:

final class FrogCodable: Codable, DefaultsSerializable {
    let name: String
 }

No implementation needed! By doing this you will get an option to specify an optional DefaultsKey:

let frog = DefaultsKey<FrogCodable?>("frog")

Additionally, you've got an array support for free:

let froggies = DefaultsKey<[FrogCodable]?>("froggies")

NSCoding

NSCoding was supported before version 4, but in this version we take the support on another level. No need for custom subscripts anymore! Support your custom NSCoding type the same way as with Codable support:

final class FrogSerializable: NSObject, NSCoding, DefaultsSerializable { ... }

No implementation needed as well! By doing this you will get an option to specify an optional DefaultsKey:

let frog = DefaultsKey<FrogSerializable?>("frog")

Additionally, you've got an array support also for free:

let froggies = DefaultsKey<[FrogSerializable]?>("froggies")

RawRepresentable

And the last but not least, RawRepresentable support! Again, the same situation like with NSCoding and Codable:

enum BestFroggiesEnum: String, DefaultsSerializable {
    case Andy
    case Dandy
}

No implementation needed as well! By doing this you will get an option to specify an optional DefaultsKey:

let frog = DefaultsKey<BestFroggiesEnum?>("frog")

Additionally, you've got an array support also for free:

let froggies = DefaultsKey<[BestFroggiesEnum]?>("froggies")

Extending existing types

Let's say you want to extend a support UIColor or any other type that is NSCoding, Codable or RawRepresentable. Extending it to be SwiftyUserDefaults-friendly should be as easy as:

extension UIColor: DefaultsSerializable {}

If it's not, we have two options:
a) It's a custom type that we don't know how to serialize, in this case at Custom types
b) It's a bug and it should be supported, in this case please file an issue (+ you can use custom types method as a workaround in the meantime)
 

Custom types

If you want to add your own custom type that we don't support yet, we've got you covered. We use DefaultsBridges of many kinds to specify how you get/set values and arrays of values. When you look at DefaultsSerializable protocol, it expects two properties in each type: _defaults and _defaultsArray, where both are of type DefaultsBridge.

For instance, this is a bridge for single value data storing/retrieving using NSKeyedArchiver/NSKeyedUnarchiver:

public struct DefaultsKeyedArchiverBridge<T>: DefaultsBridge {

    public func get(key: String, userDefaults: UserDefaults) -> T? {
        userDefaults.data(forKey: key).flatMap(NSKeyedUnarchiver.unarchiveObject) as? T
    }

    public func save(key: String, value: T?, userDefaults: UserDefaults) {
        userDefaults.set(NSKeyedArchiver.archivedData(withRootObject: value), forKey: key)
    }

    public func deserialize(_ object: Any) -> T? {
        guard let data = object as? Data else { return nil }
        return NSKeyedUnarchiver.unarchiveObject(with: data) as? T
    }    
}

Bridge for default storing/retrieving array values:

public struct DefaultsArrayBridge<T: Collection>: DefaultsBridge {
    public func save(key: String, value: T?, userDefaults: UserDefaults) {
        userDefaults.set(value, forKey: key)
    }

    public func get(key: String, userDefaults: UserDefaults) -> T? {
        userDefaults.array(forKey: key) as? T
    }

    public func deserialize(_ object: Any) -> T? {
        nil
    }
}

Now, to use these bridges in our type we simply declare it as follows:

struct FrogCustomSerializable: DefaultsSerializable {

    static var _defaults: DefaultsKeyedArchiverBridge( { DefaultsKeyedArchiverBridge() }
    static var _defaultsArray: DefaultsKeyedArchiverBridge { DefaultsKeyedArchiverBridge() }

    let name: String
}

Unfortunately, if you find yourself in a situation where you need a custom bridge, you'll probably need to write your own:

final class DefaultsFrogBridge: DefaultsBridge {
    func get(key: String, userDefaults: UserDefaults) -> FrogCustomSerializable? {
        let name = userDefaults.string(forKey: key)
        return name.map(FrogCustomSerializable.init)
    }

    func save(key: String, value: FrogCustomSerializable?, userDefaults: UserDefaults) {
        userDefaults.set(value?.name, forKey: key)
    }

    func deserialize(_ object: Any) -> FrogCustomSerializable? {
        guard let name = object as? String else { return nil }

        return FrogCustomSerializable(name: name)
    }
}

final class DefaultsFrogArrayBridge: DefaultsBridge {
    func get(key: String, userDefaults: UserDefaults) -> [FrogCustomSerializable]? {
        userDefaults.array(forKey: key)?
            .compactMap { $0 as? String }
            .map(FrogCustomSerializable.init)
    }

    func save(key: String, value: [FrogCustomSerializable]?, userDefaults: UserDefaults) {
        let values = value?.map { $0.name }
        userDefaults.set(values, forKey: key)
    }

    func deserialize(_ object: Any) -> [FrogCustomSerializable]? {
        guard let names = object as? [String] else { return nil }

        return names.map(FrogCustomSerializable.init)
    }
}

struct FrogCustomSerializable: DefaultsSerializable, Equatable {

    static var _defaults: DefaultsFrogBridge { DefaultsFrogBridge() }
    static var _defaultsArray: DefaultsFrogArrayBridge { DefaultsFrogArrayBridge() }

    let name: String
}

To support existing types with different bridges, you can extend it similarly:

extension Data: DefaultsSerializable {
    public static var _defaultsArray: DefaultsArrayBridge<[T]> { DefaultsArrayBridge() }
    public static var _defaults: DefaultsDataBridge { DefaultsDataBridge() }
}

Also, take a look at our source code (or tests) to see more examples of bridges. If you find yourself confused with all these bridges, please create an issue and we will figure something out.

Property wrappers

SwiftyUserDefaults provides property wrappers for Swift 5.1! The property wrapper, @SwiftyUserDefault, provides an option to use it with key path and options: caching or observing.

Caching means that we will store the value for you and do not hit the UserDefaults for value almost never, only for the first value fetch.

Observing means we will observe, via KVO, your property so you don't have to worry if it was saved somewhere else and you use caching.

Now usage! Given keys:

extension DefaultsKeys {
    var userColorScheme: DefaultsKey<String> { .init("userColorScheme", defaultValue: "default") }
    var userThemeName: DefaultsKey<String?> { .init("userThemeName") }
    var userLastLoginDate: DefaultsKey<Date?> { .init("userLastLoginDate") }
}

You can declare a Settings struct:

struct Settings {
    @SwiftyUserDefault(keyPath: \.userColorScheme)
    var userColorScheme: String

    @SwiftyUserDefault(keyPath: \.userThemeName, options: .cached)
    var userThemeName: String?

    @SwiftyUserDefault(keyPath: \.userLastLoginDate, options: [.cached, .observed])
    var userLastLoginDate: Date?
}

KVO

KVO is supported for all the types that are DefaultsSerializable. However, if you have a custom type, it needs to have correctly defined bridges and serialization in them.

To observe a value for local DefaultsKey:

let nameKey = DefaultsKey<String>("name", defaultValue: "")
Defaults.observe(key: nameKey) { update in
    // here you can access `oldValue`/`newValue` and few other properties
}

To observe a value for a key defined in DefaultsKeys extension:

Defaults.observe(\.nameKey) { update in
    // here you can access `oldValue`/`newValue` and few other properties
}

By default we are using [.old, .new] options for observing, but you can provide your own:

Defaults.observe(key: nameKey, options: [.initial, .old, .new]) { _ in }

KeyPath dynamicMemberLookup

SwiftyUserDefaults makes KeyPath dynamicMemberLookup usable in Swift 5.1!

extension DefaultsKeys {
    var username: DefaultsKey<String?> { .init("username") }
    var launchCount: DefaultsKey<Int> { .init("launchCount", defaultValue: 0) }
}

And just use it ;-)

// Get and set user defaults easily
let username = Defaults.username
Defaults.hotkeyEnabled = true

// Modify value types in place
Defaults.launchCount += 1
Defaults.volume -= 0.1
Defaults.strings += "… can easily be extended!"

// Use and modify typed arrays
Defaults.libraries.append("SwiftyUserDefaults")
Defaults.libraries[0] += " 2.0"

// Easily work with custom serialized types
Defaults.color = NSColor.white
Defaults.color?.whiteComponent // => 1.0

Launch arguments

Do you like to customize your app/script/tests by UserDefaults? Now it's fully supported on our side, statically typed of course.

Note: for now we support only Bool, Double, Int, String values, but if you have any other requests for that feature, please open an issue or PR and we can talk about implementing it in new versions.

You can pass your arguments in your schema:

Pass launch arguments in Xcode Schema editor.

Or you can use launch arguments in XCUIApplication:

func testExample() {
    let app = XCUIApplication()
    app.launchArguments = ["-skipLogin", "true", "-loginTries", "3", "-lastGameTime", "61.3", "-nickname", "sunshinejr"]
    app.launch()
}

Or pass them as command line arguments!

./script -skipLogin true -loginTries 3 -lastGameTime 61.3 -nickname sunshinejr

Utils

Remove all keys

To reset user defaults, use removeAll method.

Defaults.removeAll()

Shared user defaults

If you're sharing your user defaults between different apps or an app and its extensions, you can use SwiftyUserDefaults by overriding the Defaults shortcut with your own. Just add in your app:

var Defaults = DefaultsAdapter<DefaultsKeys>(defaults: UserDefaults(suiteName: "com.my.app")!, keyStore: .init())

Check key

If you want to check if we've got a value for DefaultsKey:

let hasKey = Defaults.hasKey(\.skipLogin)

Installation

Requirements

Swift version >= 4.1
iOS version >= 9.0
macOS version >= 10.11
tvOS version >= 9.0
watchOS version >= 2.0

CocoaPods

If you're using CocoaPods, just add this line to your Podfile:

pod 'SwiftyUserDefaults', '~> 5.0'

Install by running this command in your terminal:

pod install

Then import the library in all files where you use it:

import SwiftyUserDefaults

Carthage

Just add to your Cartfile:

github "sunshinejr/SwiftyUserDefaults" ~> 5.0

Swift Package Manager

Just add to your Package.swift under dependencies:

let package = Package(
    name: "MyPackage",
    products: [...],
    dependencies: [
        .package(url: "https://github.com/sunshinejr/SwiftyUserDefaults.git", .upToNextMajor(from: "5.0.0"))
    ],
    targets: [...]
)

More like this

If you like SwiftyUserDefaults, check out SwiftyTimer, which applies the same swifty approach to NSTimer.

You might also be interested in my blog posts which explain the design process behind those libraries:

Contributing

If you have comments, complaints or ideas for improvements, feel free to open an issue or a pull request.

Authors and license

Maintainer: Łukasz Mróz

Created by: Radek Pietruszewski

SwiftyUserDefaults is available under the MIT license. See the LICENSE file for more info.

Author: sunshinejr
Source Code: https://github.com/sunshinejr/SwiftyUserDefaults
License: MIT license

#ios #swift 

SwiftyUserDefaults: Modern Swift API for NSUserDefaults
 iOS App Dev

iOS App Dev

1656873600

Twitter Sentiment iOS Project Written in Swift

This is a sample project, free to use (under MIT license), enhance or fix. The main purpose of this code is for educational scenarios. Please provide feedback or questions.

About

The program runs on iOS devices, connecting to Twitter (using the Swifter API that was added via Swift Package Manager), receiving up to 100 tweets that match an entered query string. These tweets are then scored, using a NLP CoreML model (created via CreateML) to detect the sentiment (label is "-1" for negative, "0" for neutral and "1" for positive).

The result of scoring the tweets is then shown in a piechart (via the Charts package, also added to the project via SPM) together with the tweets from the selected category.

The project demonstrates a few things:

  • Usage of a ML model, created with CreateML for NLP
  • Usage of the Swifter API to receive Twitter messages
  • Separation of functionality into MVP

How to build and run

Requirements

  • XCode 13 or higher
  • iOS 15 or higher
  • Twitter Developer Account

You'll need a Twitter API key and secret to get this code running.

  1. Go to developer.twitter.com to create a developer account (or add to existing twitter account)
  2. Generate an API key and API secret. These are normally only good for the 2.0 Twitter API
  3. Request an elevated API access. Swifter uses the 1.1 API that requires the elevated access, which should still be free for experimental use.

You'll need to inject the API key and secret into the XCode project.

As you should never store, commit or upload any API keys or secret to github, you have to specifiy them separately in an xcode configuration. The main idea is to have the locally available at compile time but never to check them in to the project or github.

  1. Create a new "Configuration Settings File" in xcode (parallel to the Info.plist) and name it "APISecrets". The xcoide project configurations will include that file during compile time.
  2. That file should never be added or committed to github. It should actually already be "blacklisted" in the provided .gitignore file.
  3. Add two entries with the name "API_KEY" and "API_SECRET" to that new configuration file. The result should look something like this:
API_KEY = myAPIKeyThatIGotFromTwitter
API_SECRET = theCorrespondingAPISecret  

Download Details:
Author: draese
Source Code: https://github.com/draese/TwitterSentiment
License: MIT license

#swift #ios

Twitter Sentiment iOS Project Written in Swift
 iOS App Dev

iOS App Dev

1656866280

Space | an IOS Widget Displaying NASA's Astronomy Picture Of The Day

Space!

NASA's Astronomy Picture of the Day – now on your Home Screen with widgets!

Space! displays the latest APOD photo curated by NASA every day. See the latest photo by adding a widget to your Home Screen or Today View, or just open the app for a full-screen view and an educational description.

🌿 Space! is a fully open source app, built with Swift and SwiftUI.

📜 NASA images are in the public domain. Other images are copyright of their respective owners. More information about APOD can be found at: https://apod.nasa.gov/. Space! does not collect any user data.

📱 To learn how to use widgets, visit: https://support.apple.com/en-us/HT207122

Download Details:
Author: jtbandes
Source Code: https://github.com/jtbandes/SpacePOD
License: MIT license

#swift #ios

Space | an IOS Widget Displaying NASA's Astronomy Picture Of The Day
Jamel  O'Reilly

Jamel O'Reilly

1656861900

Storez: Safe Store-Agnostic Key-Value Storage Written in Swift

Storez

Safe, statically-typed, store-agnostic key-value storage

Highlights

Fully Customizable:
Customize the persistence store, the KeyType class, post-commit actions .. Make this framework yours!

Batteries Included:
In case you just want to use stuff, the framework is shipped with pre-configured basic set of classes that you can just use.

Portability, Check!:
If you're looking to share code between you app and extensions, watchkit, apple watch, you're covered! You can use the NSUserDefaults store, just set your shared container identifier as the suite name.

Example:

final class WeatherService {

  private enum Keys: Namespace {
    static let id = "weather-service"
    static let temperature = Key<Keys, Double>(id: "temp", defaultValue: 0.0)
  }

  private let store: UserDefaultsStore
  
  var temperature: Double {
    return store.get(Keys.temperature)
  }

  init(store: UserDefaultsStore) {
    self.store = store
  }

  func weatherUpdate(temperature: Double) {
    store.set(Keys.temperature, temperature)
  }
}

Features

Available Stores

StoreBackendSubspec
UserDefaultsStoreNSUserDefaultsStorez/UserDefaults
CacheStoreNSCacheStorez/Cache

For all stores, simply use pod "Storez"

Type-safe, store-agnostic, nestable Key definitions

// Entries must belong to a "group", for namespacing
struct Animals: Namespace {
    static let id = "animals"
}

let kingdom = Key<Animals, Void?>(id: "mammals", defaultValue: nil)
kingdom.stringValue // "animals:mammals"

// Nesting
struct Cats: Namespace {
    typealias parent = Animals
    static let id = "cats"

    // Namespaces also have pre and post commit hooks
    func preCommitHook() { /* custom code */ }
    func postCommitHook() { /* custom code */ }
}

let cat = Key<Cats, Void?>(id: "lion", defaultValue: nil)
cat.stringValue // "animals:cats:lion"

Initialize the store you want

// Use UserDefaultsStore for this example
let store = UserDefaultsStore(suite: "io.kitz.testing")
let key = Key<GlobalNamespace, Int?>(id: "key", defaultValue: nil)

// With three simple functions
store.set(key, value: 8)
store.get(key) // 8
store.clear() // Start fresh every time for testing

Optionality is honored throughout

let nullable = Key<GlobalNamespace, String?>(id: "nullable", defaultValue: nil)
store.get(nullable)?.isEmpty   // nil
store.set(nullable, value: "")
store.get(nullable)?.isEmpty   // true

let nonnull = Key<GlobalNamespace, String>(id: "nonnull", defaultValue: "!")
store.get(nonnull).isEmpty  // false
store.set(nonnull, value: "")
store.get(nonnull).isEmpty  // true

Custom objects easily supported

NEW Simply conform to Codable!

(You can still use UserDefaultsConvirtable if needed)

struct CustomObject: Codable {
    var strings: [String]
}

// custom objects properly serialized/deserialized
let customObject = CustomObject(
    strings: ["fill", "in", "the"]
)

// let's add a processing block this time
let CustomValue = Key<GlobalNamespace, CustomObject?>(id: "custom", defaultValue: nil) {

    var processedValue = $0
    processedValue?.strings.append("blank!")
    return processedValue
}

store.set(CustomValue, value: customObject)
store.get(CustomValue)?.strings.joinWithSeparator(" ") // fill in the blank!

Make your own KeyType

// For example, make an key that emits NSNotifications
struct MyKey<G: Namespace, V>: KeyType {

    typealias NamespaceType = G
    typealias ValueType = V

    var stringValue: String
    var defaultValue: ValueType

    func didChange(oldValue: ValueType, newValue: ValueType) {
        NSNotificationCenter.defaultCenter().postNotificationName(stringValue, object: nil)
    }
}

Getting Started

Swift Package Manager

You can add Storez to an Xcode project by adding it as a package dependency.

Depending on how your project is structured:

  • If you have a single application target that needs access to the library, then add Storez directly to your application.
  • If you want to use this library from multiple Xcode targets, or mixing Xcode targets and SPM targets, you likely want to create a shared framework that depends on Storez and then depend on that framework in all of your targets.

To use Storez in a Package.swift file, add this to the dependencies: section.

.package(url: "https://github.com/SwiftKitz/Storez.git", .upToNextMinor(from: "3.0.0")),

CocoaPods

CocoaPods is fully supported. You can choose which store you want to use (see above). Simply add the following line to your Podfile:

pod 'Storez/UserDefaults'

Motivation

I've seen a lot of great attempts at statically-types data stores, but they all build a tightly coupled design that limits the end-developer's freedom. With this framework, you can start prototyping right away with the shipped features, then replace the persistence store and KeyType functionality with your heart's content and keep your code the way it is!

Author: SwiftKitz
Source Code: https://github.com/SwiftKitz/Storez
License: MIT license

#ios #swift 

Storez: Safe Store-Agnostic Key-Value Storage Written in Swift
 iOS App Dev

iOS App Dev

1656858960

Scribe IOS: App with Keyboards for Language Learners

iOS app with keyboards for language learners

Scribe-iOS is a pack of iOS and iPadOS keyboards for language learners. Features include translation (beta), verb conjugation and word annotation that give users the tools needed to communicate with confidence.

Scribe is fully open-source and does not collect usage data or ask for system access. Feature data is sourced from Wikidata and stored in-app, meaning Scribe is a highly responsive experience that does not require an internet connection.

The contributing section has information for those interested, with the articles and presentations in featured by also being a good resource for learning more about Scribe.

Preview Videos

The following are combined preview videos for the App Store:

iPhone 6.5" version

https://user-images.githubusercontent.com/24387426/176200294-e9ce0ed0-f861-4ca4-8281-5c53d01048d3.mp4

See also

iPad Pro 4th gen version

https://user-images.githubusercontent.com/24387426/176200444-7b85488b-55a9-40d9-aa8a-915a77ae2c7f.mp4

The App Store videos, images and text can be found in Resources/AppStore. Contributions to improve them are welcome, but please open an issue to check before.

Supported Languages

Scribe's goal is functional, feature-rich keyboards for all languages. Check scribe_data/extract_transform for queries for currently supported languages and those that have substantial data on Wikidata. Also see the new keyboard label in the Issues for keyboards that are currently in progress or being discussed, and suggest a new keyboard if you don't see it being worked on already!

The following table shows the supported languages and the amount of data available for each on Wikidata:

LanguagesNounsVerbsTranslations*Adjectives†Prepositions‡
French16,2331,50167,652--
German28,7453,38167,652-187
Italian8477367,652--
Portuguese5,10949567,652--
Russian194,4011167,652-12
Spanish20,9873,71767,652--
Swedish42,0024,16067,652--

* Given the current beta status where words are machine translated.

Adjective-preposition support is in progress (see issue).

Only for languages for which preposition annotation is needed.

Updates to the above data can be done using scribe_data/load/update_data.py.

Setup

Users access Scribe language keyboards through the following:

  • Download Scribe from the App Store
  • Settings -> General -> Keyboard -> Keyboards -> Add New Keyboard
  • Select Scribe and choose from the available language keyboards
  • When typing press 🌐 to select keyboards

Keyboard Features

Keyboard features are accessed via the Scribe key at the top left of any Scribe keyboard. Pressing this key gives the user three new selectable options: Translate, Conjugate and Plural in the keyboard's language. These keys allow for words to be queried and inserted into the text field followed by a space.

Current features include:

• Translation

The beta Translate feature can translate single words or phrases from English into the language of the current keyboard when the return key is pressed.

Those interested in improving this feature can see the Translation project. The goal is that Translate will provide options for entered words where a user can use grammatical categories and synonyms to select the best option (see issue). Then the feature will expand to allow translations from system and chosen languages. More advanced methods will be planned once this feature is out of beta.

As of now translations are not widely available on Wikidata (see issue). The current functionality is thus based on 🤗 Transformers machine translations of words queried from Wikidata. The ultimate goal is for the translations and synonyms to all be directly queried.

• Verb Conjugation

With the Conjugate feature, a user is presented with the grammar charts for an entered verb instead of the keyboard. Pressing an example in the charts inserts the chosen conjugation into the text field.

• Noun Plurals

The Plural feature allows a user to enter a noun and then insert its plural into the text field when the return key is pressed.

• Word Annotation

Scribe further annotates words in the command bar to help users understand the context of what they're typing. Annotations are displayed once a user has typed a given word and pressed space or by pressing the Scribe key while it is selected. The hope is that annotation will help a user remember grammar rules even when not using Scribe.

Nouns

Scribe annotates nouns in the command bar according to the following conventions:

  • Feminine: colored red 🟥 and marked with (F)
  • Masculine: colored blue 🟦 and marked with (M)
  • Common: colored purple 🟪 and marked with (C)
  • Neutral: colored green 🟩 and marked with (N)
  • Plural: colored orange 🟧 and marked with (PL)
  • More than one: marked with all their forms

The above form abbreviations are translated into their equivalents in the keyboard's language.

Prepositions

Scribe also annotates the grammatical cases (accusative, dative, etc) of prepositions in the command bar if there is a relation in the given language.

• Base Functionality

The goal is for Scribe to have all the functionality of system keyboards. See the Base Keyboard Features project if interested in helping.

Current features

  • iPhone and iPad support
  • Dynamic layouts for cross-device performance
  • Portrait and landscape modes
  • Dark mode compatibility
  • Autocompletion and correction (WIP - see Autocomplete and Autocorrect issues)
  • Auto-capitalization following ., ? and !
  • The double space period shortcut
  • Typing symbols and numbers followed by a space returns keyboard to letters
  • Hold-to-select characters for letters and symbols
  • Key pop up views for letters and symbols

Language Practice

A future feature of Scribe is language practice within the app itself. Scribe presents users with information that is directly relevant to their current struggles with a second language. This information can be saved in-app and used to create personalized lessons such as flashcards to reinforce the information that Scribe has provided. Work on this feature will be completed in the language practice project.

Contributing

Work that is in progress or could be implemented is tracked in the issues and projects. Please see the contribution guidelines if you are interested in contributing to Scribe-iOS. This document has all necessary information and also includes steps to set up a development environment.

Those interested can further check the -next release- and -priority- labels in the issues for those that are most important, as well as those marked good first issue that are tailored for first time contributors.

Ways to Help

Data Edits

Scribe does not accept direct edits to the grammar JSON files as they are sourced from Wikidata. Edits can be discussed and the Scribe-Data queries will be changed and ran before an update. If there is a problem with one of the files, then the fix should be made on Wikidata and not on Scribe. Feel free to let us know that edits have been made by opening a data issue or contacting us in the issues for Scribe-Data and we'll be happy to integrate them!

Featured By

Articles and Presentations on Scribe

2022

Download Details:
Author: scribe-org
Source Code: https://github.com/scribe-org/Scribe-iOS
License: GPL-3.0 license

#swift #ios

Scribe IOS: App with Keyboards for Language Learners
 iOS App Dev

iOS App Dev

1656858720

Prayer in English | Help Understand The Spoken Text In Islamic Prayers

About

You regularly pray your prayers in Arabic, but do not really understand what you're saying?

This app will help you make your prayers in English (or German or Turkish) to make them more meaningful to you. To do this, leave your device lying in front of you during the prayer and simply read the text from there. Or alternatively, configure the computer voice audio mode and listen to the text with your headphones during the prayer, while keeping your phone in your pocket. The app is purposely kept simple and reduced to the basic functions so that it does not contain distracting elements such as animations or advertising.

Donation

Prayer was brought to you by Cihat Gündüz in his free time. If you want to thank me and support the development of this project, please make a small donation on PayPal. In case you also like my other open source contributions and articles, please consider motivating me by becoming a sponsor on GitHub or a patron on Patreon.

Thank you very much for any donation, it really helps out a lot! 💯

Contributing

Contributions are welcome. Feel free to open an issue on GitHub with your ideas or implement an idea yourself and post a pull request. If you want to contribute code, please try to follow the same syntax and semantic in your commit messages (see rationale here).

Download Details:
Author: FlineDev
Source Code: https://github.com/FlineDev/Prayer
License: GPL-3.0 license

#swift #ios

Prayer in English | Help Understand The Spoken Text In Islamic Prayers
 iOS App Dev

iOS App Dev

1656851340

Population Clock: IOS tool for Learning About Geography & Demographics

Population Clock

Population Clock is an iOS tool for learning about geography and demographics.

Download app on iTunes

The project uses the most recent data from these two trusted sources:

Besides the iOS app, there are a few Python scripts to execute the following actions:

  • Scrape the country descriptions and energy production from the World Factbook.
  • Download the most recent indicators from World Databank using their JSON API (Some updates are quarterly and others yearly).
  • Generate plist file with all indicators and descriptions to use with Xcode.
  • With population growth data, create colored SVG map.
  • Generate 8-bit greyscale map with one color per country. This allows us to identify which country was clicked by reading the color of the selected pixel.

Here is a list of the metrics displayed in the app. Some countries do not have all the data.

  • Access to electricity (% of population)
  • Birth rate, crude (per 1,000 people)
  • CO2 emissions (kt)
  • Death rate, crude (per 1,000 people)
  • Energy Production (kWh)
  • Fertility rate, total (births per woman)
  • Forest area (% of land area)
  • GDP (constant LCU)
  • GDP growth (annual %)
  • GDP per capita (constant LCU)
  • Health expenditure, total (% of GDP)
  • Internet users (per 100 people)
  • Life expectancy at birth, total (years)
  • Mobile cellular subscriptions (per 100 people)
  • Passenger cars (per 1,000 people)
  • Population growth (annual %)
  • Population, total

Download Details:
Author: Netfilter-Com
Source Code: https://github.com/Netfilter-Com/PopulationClock
License: BSD-3-Clause license

#swift #ios 

Population Clock: IOS tool for Learning About Geography & Demographics
 iOS App Dev

iOS App Dev

1656851160

Pocket Code | IOS Implementation Of The Catrobat Language

Catty

Catty, also known as Pocket Code for iOS, is an on-device visual programming system for iPhones.

Catrobat is a visual programming language and set of creativity tools for smartphones, tablets, and mobile browsers. Catrobat programs can be written by using the Catroid programming system on Android phones and tablets, using Catroid, or Catty for iPhones.

For more information oriented towards developers please visit our developers page.

Issues

For reporting issues use our Jira issue tracker. Before creating a new bug, please browse our currently open issues here.

Contributing

We welcome all offers for help! If you want to contribute we suggest that you start with forking our repository and browse the code. You can then look at our Jira issue tracker and start working on a ticket. It is recommended to start your contribution on a ticket labelled as TRAINING or BEGINNER ticket. We strictly use Test-Driven Development and Clean Code, code developed in a different style will not be accepted. After you have implemented a certain ticket, hand in a pull request on GitHub and have a look at our pull request template.

If you want to implement a new feature, please ask about further details on Google Groups.

Learn iOS development

We recommend Developing iOS 11 Apps with Swift from the Stanford University.

Start setting up the working environment:

  • Install Xcode 13.0 or newer, Carthage and SwiftLint.
  • Clone this repository, set up the required third-party libraries by executing make init within the 'Catty' directory and open Catty.xcodeproj
  • If you have any further questions please use our Google Group

Download Details:
Author: catrobat
Source Code: https://github.com/catrobat/catty
License:

#swift #ios

Pocket Code | IOS Implementation Of The Catrobat Language
Jamel  O'Reilly

Jamel O'Reilly

1656849660

Prephirences: A Swift Library That Provides Useful Protocols

Prephirences - Preϕrences 

Prephirences is a Swift library that provides useful protocols and convenience methods to manage application preferences, configurations and app-state.

  @Preference(key: "enabled")
  var enabled: Bool?

  @UserDefaultsPreference(key: "my.string.pref")
  var pref: String?

  @MutablePreference(preferences: UserDefaults.standard, key: "enabled")
  var enabled: Bool?
let userDefaults = UserDefaults.standard
if let enabled = userDefaults["enabled"] as? Bool {..}
userDefaults["mycolorkey", archive] = UIColor.blue

Preferences is not only UserDefaults, it could be also :

  • Keychain to store credential
  • Any dictionary
  • Application information from Bundle
  • File stored preferences (ex: plist)
  • iCloud stored preferences NSUbiquitousKeyValueStore
  • or your own private application preferences

ie. any object which implement the simple protocol PreferencesType, which define key value store methods.

You can also combine multiples preferences and work with them transparently (see Composing)

Creating

The simplest implementation of PreferencesType is DictionaryPreferences

// From Dictionary
var fromDico = DictionaryPreferences(myDictionary)
// or literal
var fromDicoLiteral: DictionaryPreferences = ["myKey": "myValue", "bool": true]

// From filepath
if let fromFile = DictionaryPreferences(filePath: "/my/file/path") {..}
// ...in main bundle ##
if let fromFile = DictionaryPreferences(filename: "prefs", ofType: "plist") {..}

Accessing

You can access with all methods defined in PreferencesType protocol

if let myValue = fromDicoLiteral.object(forKey: "myKey") {..}
if let myValue = fromDicoLiteral["bool"] as? Bool {..}

var hasKey = fromDicoLiteral.hasObject(forKey: "myKey")
var myValue = fromDicoLiteral.bool(forKey: "myKey")
..

If you want to access using RawRepresentable enum.

enum MyKey: PreferenceKey/*String*/ {
   case Key1, Key2, ...
}
if let myValue = fromDicoLiteral.object(forKey: MyKey.Key1) {..}
var myValue = fromDicoLiteral.bool(forKey: MyKey.Key2)

:warning: RawRepresentableKey must be imported, see setup.

Modifying

Modifiable preferences implement the protocol MutablePreferencesTypes

The simplest implementation is MutableDictionaryPreferences

var mutableFromDico: MutableDictionaryPreferences = ["myKey": "myValue"]

mutableFromDico["newKey"] = "newValue"
mutableFromDico.set("myValue", forKey: "newKey")
mutableFromDico.set(true, forKey: "newKey")
...

You can append dictionary or other PreferencesType using operators

mutableFromDico += ["newKey": "newValue", "otherKey": true]

You can also remove one preference

mutableFromDico -= "myKey"

Apply operators to one preference

You can extract a MutablePreference from any MutablePreferencesTypes and apply operators according to its value type

var intPref: MutablePreference<Int> = aPrefs.preference(forKey: "intKey")
var intPref: MutablePreference<Int> = aPrefs <| "intKey"

intPref++
intPref--
intPref += 30
intPref -= 30
intPref *= 20
intPref %= 7
intPref /= 3

switch(intPref) {
   case 1: println("one")
   case 2...10: println("not one or zero but...")
   default: println("unkwown")
}

var boolPref: MutablePreference<Bool> = aPrefs <| "boolKey")

boolPref &= false
boolPref |= true
boolPref != true

You can also use some methods to change value

var stringPref: MutablePreference<String> = userDefaults <| "stringKey"
stringPref.apply { value in
  return value?.uppercaseString
}

or transform the value type using closures

let intFromBoolPref : MutablePreference<Int> = boolPref.transform { value in
  return (value ?? false) ? 1:0
}

Transformation and archiving

Before storing or accessing the value, transformation could be applied, which conform to protocol PreferenceTransformation.

This allow to archive, to change type, return default value if nil and many more.

You can get and set value using subscript

userDefaults["aKey", myTransformation] = myObject

if let object = userDefaults["aKey", myTransformation] {...}

If you extract one preference, use transformation property to setup the transformation

var aPref: MutablePreference<MyObject> = userDefaults <| "aKey"
aPref.transformation = myTransformation

or you can use some utility functions to specify a default value when the stored value match a condition

public var intValueMin10: MutablePreference<Int> {
  get {
    return userDefaults.preference(forKey: "intKey")
          .whenNil(use: 100)
          .ensure(when: lessThan100, use: 100)
  }
  set {..}
}

Archiving

Archiving is particularly useful with NSUserDefaults because NSUserDefaults can't store all type of objects. The following functions could help by transforming the value into an other type

You can archive into Data using this two methods

userDefaults.set(objectToArchive: UIColor.blueColor(), forKey: "colorKey")
userDefaults["colorKey", .Archive] = UIColor.blueColor()

and unarchive using

if let color = userDefaults.unarchiveObject(forKey: "colorKey") as? UIColor {..}
if let color = userDefaults["colorKey", .Archive]  as? UIColor {..}

If you extract one preference, use transformation property to setup archive mode

var colorPref: MutablePreference<UIColor> = userDefaults <| "colorKey"
colorPref.transformation = TransformationKey.Archive
colorPref.value = UIColor.redColor()
if let color = colorPref.value as? UIColor {..}

NSValueTransformer

You can also apply for all objects type an NSValueTransformer, to transform into JSON for instance

userDefaults["colorKey", myValueTransformerToJson] = myComplexObject

if let object = userDefaults["colorKey", myValueTransformerToJson] {...}

:warning: allowsReverseTransformation must return true

Store RawRepresentable objects

For RawRepresentable objects like enum you can use the computed attribute preferenceTransformation as transformation

enum PrefEnum: String {
    case One, Two, Three
}
var pref: MutablePreference<PrefEnum> = preferences <| "enumKey"
pref.transformation = PrefEnum.preferenceTransformation
pref.value = PrefEnum.Two

Some implementations

UserDefaults

UserDefaults implement PreferencesType and can be acceded with same methods

let userDefaults = UserDefaults.standard

if let myValue = userDefaults["mykey"] as? Bool {..}

NSUserDefaults implement also MutablePreferencesType and can be modified with same methods

userDefaults["mykey"] = "myvalue"
// with type to archive
userDefaults["mykey", .Archive] = UIColor.blueColor()

Bundle

All Bundle implement PreferencesType, allowing to access Info.plist file.

For instance the Bundle.main contains many useful informations about your application.

Prephirences framework come with some predefined enums described in apple documentations and defined in PropertyListKeys.swift

let bundle = Bundle.main
let applicationName = bundle[.CFBundleName] as? String

NSUbiquitousKeyValueStore

To store in iCloud, NSUbiquitousKeyValueStore implement also PreferencesType

See composing chapter to merge and synchronize iCloud preferences with other preferences.

Key Value Coding

Foundation classes

You can wrap an object respond to implicit protocol NSKeyValueCoding in KVCPreferences or MutableKVCPreferences

let kvcPref = MutableKVCPreferences(myObject)

Be sure to affect the correct object type

Swift classes

Using ReflectingPreferences you can easily access to a struct or swift class. Just add extension.

struct PreferenceStruct {
    var color: String = "red"
    var age: Int
    let enabled: Bool = true
}
extension PreferenceStruct: ReflectingPreferences {}

You can then use all functions from PreferencesType

var pref = PreferenceStruct(color: "red", age: 33)
if pref["color"] as? String { .. }

Core Data

You can wrap on NSManageObject in ManageObjectPreferences or MutableManageObjectPreferences

let managedPref = ManageObjectPreferences(myManagedObject)

Plist

There is many way to play with plist files

  • You can use Plist (with the useful write method)
  • You can init DictionaryPreferences or MutableDictionaryPreferences with plist file
  • You can read dictionary from plist file and use set(dictionary: on any mutable preferences

Keychain

To store into keychain, use an instance of KeychainPreferences

KeychainPreferences.sharedInstance // default instance with main bundle id
var keychain = KeychainPreferences(service: "com.github.example")

then store String or Data

keychain["anUserName"] = "password-encoded"

if let pass = keychain.stringForKey("anUserName") {..}

Accessibility

keychain.accessibility = .AccessibleAfterFirstUnlock

Sharing Keychain items

keychain.accessGroup = "AKEY.shared"

NSCoder

NSCoder is partially supported (dictionary is not available)

When you implementing NSCoding you can do

init?(coder decoder: NSCoder) {
  self.init()
  self.intVar = decoder["intVarKey"] as? Int ?? 0
  // or self.intVar = decoder.integer(forKey: "intVar")
  self.stringVar = decoder["stringVarKey"] as? String ?? ""
}

func encodeWithCoder(coder: NSCoder) {
  coder["intVarKey"] = self.intVar
  coder["stringVarKey"] = self.stringVar
}

Custom implementations

Preferences

Create a custom object that conform to PreferencesType is very easy.

extension MyCustomPreferences: PreferencesType {
    func object(forKey: String) -> Any? {
        // return an object according to key
    }
    func dictionary() -> [String : Any] {
        // return a full dictionary of key value
    }
}

Only two functions are mandatory, others are automatically mapped but can be overrided for performance or readability.

  • In the same way you can implement MutablePreferencesType with set and removeObject(forKey: methods.
  • If you structure give a list of keys instead of a full dictionary, you can instead conform to PreferencesAdapter and implement func keys() -> [String].
  • You have a collection of object with each object could define a key and a value take a look at CollectionPreferencesAdapter or see NSHTTPCookieStorage implementation.

Accessing using custom key

Instead of using string or string constants, you can use an enum to define a list of keys

First create your enum with String raw value

enum MyEnum: String {
  case MyFirstKey
  case MySecondKey
}

Then add a subscript for your key

extension PreferencesType {
    subscript(key: MyEnum) -> Any? {
        return self[key.rawValue]
    }
}

Finally access your information

if let firstValue = bundle[.MyFirstKey] {..}

You can do the same with MutablePreferencesType

Proxying preferences with prefix

You can defined a subcategory of preferences prefixed with your own string like that

let myAppPrefs = MutableProxyPreferences(preferences: userDefaults, key: "myAppKey.")
// We have :
userDefaults["myAppKey.myKey"] == myAppPrefs["myKey"] // is true

This allow prefixing all your preferences (user defaults) with same key

Composing

Composing allow to aggregate multiples PreferencesType objects into one PreferencesType

let myPreferences = CompositePreferences([fromDico, fromFile, userDefaults])
// With array literal
let myPreferences: CompositePreferences = [fromDico, fromFile, userDefaults]

// Mutable, only first mutable will be affected
let myPreferences: MutableCompositePreferences = [fromDico, fromFile, userDefaults]

You can access or modify this composite preferences like any PreferencesType.

  1. When accessing, first preferences that define a value for a specified key will respond
  2. When modifying, first mutable preferences will be affected by default, but you can set MutableCompositePreferences attribute affectOnlyFirstMutable to false to affect all mutable preferences, allowing you for instance to duplicate preferences in iCloud

The main goal is to define read-only preferences for your app (in code or files) and some mutable preferences (like UserDefaults, NSUbiquitousKeyValueStore). You can then access to one preference value without care about the origin.

Managing preferences instances

If you want to use Prephirences into a framework or want to get a Preferences without adding dependencies between classes, you can register any PreferencesType into Prephirences

as shared instance

Prephirences.sharedInstance = myPreferences

or by providing an Hashable key

Prephirences.register(preferences: myPreferences, forKey: "myKey")
Prephirences.instances()["myKey"] = myPreferences
Prephirences.instances()[NSStringFromClass(self.dynamicType)] = currentClassPreferences

Then you can access it anywhere

if let pref = Prephirences.instance(forKey: "myKey") {..}
if let pref = Prephirences.instances()["myKey"] {..}

Remote preferences

By using remote preferences you can remotely control the behavior of your app.

If you use Alamofire, Alamofire-Prephirences will help you to load preferences from remote JSON or Plist

Encrypt your preferences

You can use framework CryptoPrephirences to encrypt/decrypt your preferences using cipher from CryptoSwift

Setup

Using Cocoapods

CocoaPods is a centralized dependency manager for Objective-C and Swift. Go here to learn more.

Add the project to your Podfile.

use_frameworks!

pod 'Prephirences'

Run pod install and open the .xcworkspace file to launch Xcode.

For core data

Add pod 'Prephirences/CoreData'

For RawRepresentable key

Add pod 'Prephirences/RawRepresentableKey'

For PropertyListKeys

Add pod 'Prephirences/Keys'

Using Carthage

Carthage is a decentralized dependency manager for Objective-C and Swift.

Add the project to your Cartfile.

github "phimage/Prephirences"

Run carthage update and follow the additional steps in order to add Prephirences to your project.

Using xcode project

  1. Drag Prephirences.xcodeproj to your project/workspace or open it to compile it
  2. Add the Prephirences framework to your project

Author: phimage
Source Code: https://github.com/phimage/Prephirences
License: MIT license

#ios #swift 

Prephirences: A Swift Library That Provides Useful Protocols
 iOS App Dev

iOS App Dev

1656843840

My First Memory | Memory Game, includes Introduction to IOS & Swift

📱 My first Memory 🤔💭

An introduction to iOS development with Swift.

A memory game implementation fetching images from Instagram. This project aims to introduce you to iOS development with Swift disregarding of your current skill level.

The app defaults to fetch the Instagram images of Taylor Swift (Alex: "Hehe he he... 😁, yes Miriam, that is funny"), but you can enter your own or another custom Instagram account and fetch images for it instead.

Installation

OBS! This project has been migrated to Swift 3.0, so you need Xcode 8 to open it

There is no need for any installation per se, you only need to download this project. You download the project by pressing the big green Clone or download button you see in the top right side of this page. You can either download the project as a zip, or you can use git to download the project by opening the terminal and entering:

git clone <PASTE_GITHUB_URL_HERE> 

After you have download the project, open the file called SwiftIntro.xcworkspace (not SwiftIntro.xcodeproj).

iOS development

All the screens you see are called UIViewController which consists of smaller view elements called UIView. Buttons (UIButton), text labels (UILabel), textfield for text input (UITextField) are all subclasses of the superclass UIView. All instances of UIViewController have a view (UIView), which is the root view, the canvas in which you can add buttons, labels and lists (UITableView).

iOS development follows the architecture called MVC by default, which stands for Model-View-Controller.

MVC

The Model, the View and the Controller are three different areas of responsibility within the app.

The idea is that the Controller acts as a coordinator, fetching data from either internet or a local database, stored in Models and passing those models into Views that can display the data.

ViewControllers (View and Controller)

For every screen in your app you create your own subclass of UIViewController. To that you add all the views you want your screen to consist of. You can do this in two ways, either you do it using InterfaceBuilder or in code.

  1. In InterfaceBuilder (IB in short), which is a great drag and drop tool which aims to be a WYSIWYG (What You See Is What You Get). Allthough in Xcode 7.x.x IB is not capable of rendering all the views and different styling of those (rounded corners, blur effect etc..). Xcode 8 will be more capable os this. There are two different ways to use IB you can either use Storyboards (UIStoryBoard) or you can use .xib files (also called nib files).
  2. In a storyboard you can create several UIViewControllers, maybe you create a SignInViewController, SignUpViewController and a ResetPasswordViewController. In the Storyboard you can create flows between these ViewControllers, e.g. you can create a Reset Password UIButton in the SignInViewController and then you can define that when the user presses that button, the ResetPasswordViewController is shown to the user. These flows between UIViewControllers are called Segue (pronounced "segway").
  3. The Xib file approach is an older technique which only allows for one UIViewController per Xib file. In Xib files you don't need to put UIViewControllers, you can instead put UIViews. The advantage of this is that they can be used by different UIViewControllers or UIViews in your project. So they are reusable through out the project. A good example of when to use Xib files is when you want to create a list (UITableView) or grid (UICollectionView) of views. This project uses a UICollectionView for displaying memory cards in a grid. The cards are UICollectionViewCells (subclass of UIViews...). So each item in a list or grid of view is called a cell. It is recommended to create each unique cell class in a separate Xib file.
  4. Creating views in code (or programmatically as you often say...). All UIButtons, UILabels, UITableviews, UITextViews, UITextField etc you drag and drop in Interface Builder can also be created and added to your view using pure Swift code. The syntax for this is typically:
	private func myMethodCreatingLabel() {
		let resetPasswordLabel = UILabel()
		resetPasswordLabel.text = "Have you forgot your password?"
		resetPasswordLabel.textAlignment = .Center
		resetPasswordLabel.textColor = UIColor.redColor()
		view.addSubview(resetPasswordLabel)
		/* Here we should proceed with creating NSLayoutConstraints
		which are rules that tells iOS where to place the view and
		how big the view should be.
		*/
	}

Model

A model is a struct or class that holds data. In this project we fetch data, sent over HTTP GET on the JSON format from Instagram. The images from Instagram are stored in a struct called Cards.swift. Structs and classes may seem very similar, and in terms of syntax they are. But the behave very differently in terms of memory and reference, after you have worked with this project you can have a look at this WWDC video explaining the difference.

How to write good code

iOS apps actually have a quite confusing MVC pattern, because the UIViewController is the controller, but it also has its own UIView, so in a way the UIViewController is also the view 😬😅. The MVC patterin in iOS has often been critized (here, here and here) and called Massive-View-Controller, because the UIViewController classes you create tend grow to many hundreds lines of code. This project aims to not have any Massive UIViewController. The project has four UIViewControllers (GameVC, SettingsVC, GameOverVC and LoadingDataVC) and the biggest is not even 100 lines of code. Try to aim for that less than 100 lines of code! Unfortunatly it's rare to work in a project where any UIViewController is less than 100 lines of code. So if you make it a habbit then you will be a skilled iOS developer from start 🦄. A great way of achieving small UIViewControllers is to split a single screen into multiple UIViewControllers, or to use extensions, here is a great article on how extensions of UIViewController can make your UIViewControllers smaller.

Another general guideline is to try to keep under less than 200 lines of code for all files (classes, structs or enums). When you notice that a class grows, maybe you can try to split it into two or three classes instead. In fact all files in this project is less than 100 lines of code, with one exception - MemoryDataSourceAndDelegate - which still is less than 200 lines.

SwiftLint

A good way to enforce writing good code is to install a tool called SwiftLint which we have used durint the development of this project. If you have Homebrew installed you can install it using this terminal command:

brew install swiftlint

Tasks

🐌 This looks interesting

Change the color ❤️💛💚💙💜 of the Play! button.

Change the the backgroundcolor of the cards.
(tip: check out CardCVCell.xib or CardCVCell.swift)

Change the duration of the flip card animation.

Change the username placeholder.
(tip: check out the method setupLocalizableStrings() in SettingsVC, you also need to check out the file called Localizable.strings for each language)

Add support for your third favourite language 🇫🇷🇸🇾🇯🇵, you need to test this as well (by pressing ⌘+⬆+H in the simulator you go to its home screen, where you can find the Settings app where you have to change the system language to the one you just added.)

Change the flip card animation from using a horizontal flip to a vertical. 
(tip: check out the flipCard() method in the CardCVCell class. Here is the documentation for animations)

🐰 I think I've got a good grip of it

  1. Change the Quit button title, which currently is a text with the char X, to use an image 🏔 instead.
  2. Set the background of the memory Card to be show an image 🏔 instead of just a color.
    (tip: check out CardCVCell.xib or CardCVCell.swift)
  3. In the section How to write good code we discussed the goal of writing small files, and the class MemoryDataSourceAndDelegate with its almost 200 lines of code was mentioned. Can you split this class into several smaller classes that makes sense, so that no class is more than 100 lines?
  4. Switch the position of the Restart button with the Quit button.
    (tip: don't delete the buttons... 😜 then you have to recreate the Segues ...)
  5. Save the best score (lowest clickCount for each level) a user has scored and present this score in the GameOverVC
    (Tip: Checkout NSUserDefaults for saving.)
  6. It is currently possible for a user to flip a third card while the flip animation of the two previous cards has not yet finished. Address this issue.
  7. Create a timer ⏲ NSTimer that counts the time for a game session. Display this time in the GameOverVC after the game has finished.

🦄 Bring it on

  1. Display a timer ⏲ NSTimer (could be the one you created in task 8 in I think I've got a good grip of it ) that is counting upwards in the GameVC showing elapsed time ⏰ since game start.
  2. When you press the Restart button from GameOverVC the cards will have the same position as before, this makes it easy to cheat! Your task is to shuffle the cards before restarting the game.
  3. Implement white space handling for the username textfield.
  4. Change the feedback message in GameOverVC from Well done to a dynamic title that changes according to how well it went. Some examples strings: Awesome, Not sooo bad, That was Horrible etc. This string should not be directly dependent on only Level, or only clickCount, but rather..?
  5. Currently the project uses hard coded keys for localized strings (the key itself is a string), which is the standard iOS pattern - but it smells! Instead you can introduce an Enum for all the localized strings. So that you will be able to write something like this:
	restartButton.setLocalizedTitle(LocalizedStrings.Restart)

(Tip: Either you do this by yourself, or you can use SwiftGen for this, if you know how to install it...)

6.  Add some Error ☠ handling, e.g displaying a message if the Instagram username doesn't exist, or if no images could be loaded.

7.   Make it possible to set the number of cards to a custom number. Currently the number of cards are determined base on which difficulty level you chose in the SettingsVC.

8.   Enable Landscape mode for all the views.

9.   Fetch the images from another source than Instagram. Maybe you can fetch the images from FourSquare, using its API, Flickr or Twitter. Please note all the above mentioned alternative image sources require an API token/secret. So you have to create those in order to use the APIs. Then you have to change the JSON parsing of the Cards model. You also need to modify the Photos 📷 route in the Router class to go against the correct URL.

Download Details:
Author: Sajjon
Source Code: https://github.com/Sajjon/SwiftIntro
License: GPL-3.0 license

#swift #ios

My First Memory | Memory Game, includes Introduction to IOS & Swift
Jamel  O'Reilly

Jamel O'Reilly

1656838200

Zephyr: Effortlessly Synchronize UserDefaults over iCloud

Zephyr 🌬️

Effortlessly sync UserDefaults over iCloud

About

Zephyr synchronizes specific keys and/or all of your UserDefaults over iCloud using NSUbiquitousKeyValueStore.

Zephyr has built in monitoring, allowing it to sync specific keys in the background as they change.

For the latest updates, refer to the Releases tab.

Features

  •  CocoaPods and SwiftPM Support
  •  Syncs specific UserDefaults keys or all of your UserDefaults.
  •  Background monitoring and synchronization between UserDefaults and NSUbiquitousKeyValueStore
  •  Detailed Logging

Installation Instructions

Swift VersionBranch NameWill Continue to Receive Updates?
5.1+masterYes
5.0swift5.0No
4.2swift4.2No
4.1swift4.1No
3.2swift3.2No
3.1swift3.1No

CocoaPods

pod 'Zephyr' # Swift 5.1+
pod 'Zephyr', :git => 'https://github.com/ArtSabintsev/Zephyr.git', :branch => 'swift5.0' # Swift 5.0
pod 'Zephyr', :git => 'https://github.com/ArtSabintsev/Zephyr.git', :branch => 'swift4.2' # Swift 4.2
pod 'Zephyr', :git => 'https://github.com/ArtSabintsev/Zephyr.git', :branch => 'swift4.1' # Swift 4.1
pod 'Zephyr', :git => 'https://github.com/ArtSabintsev/Zephyr.git', :branch => 'swift3.2' # Swift 3.2
pod 'Zephyr', :git => 'https://github.com/ArtSabintsev/Zephyr.git', :branch => 'swift3.1' # Swift 3.1

Swift Package Manager

.Package(url: "https://github.com/ArtSabintsev/Zephyr.git", majorVersion: 3)

Manual

  1. Download Zephyr
  2. Copy Zephyr.swift into your project.

Setup

Turn on iCloud Sync in Xcode

In Xcode, open your app's project/workspace file:

  • Click on your Project
  • Click on one of your Targets
  • Click on Capabilities
  • Turn on iCloud syncing
  • Under Services, make sure to check Key-value storage
  • Repeat for all Targets (if necessary)

How to turn on iCloud Key Value Store Syncing

Integrate Zephyr into your App

Before performing each sync, Zephyr automatically checks to see if the data in UserDefaults or NSUbiquitousKeyValueStore is newer. To make sure there's no overwriting going on in a fresh installation of your app on a new device that's connected to the same iCloud account, make sure that your UserDefaults are registered BEFORE calling any of the Zephyr methods. One way to easily achieve this is by using the UserDefaults Register API.

Sync all UserDefaults

Zephyr.sync()

Sync a specific key or keys (Variadic Option)

Zephyr.sync(keys: "MyFirstKey", "MySecondKey", ...)

Sync a specific key or keys (Array Option)

Zephyr.sync(keys: ["MyFirstKey", "MySecondKey"])

Add/Remove Keys for Background Monitoring (Variadic Option)

Zephyr.addKeysToBeMonitored(keys: "MyFirstKey", "MySecondKey", ...)
Zephyr.removeKeysFromBeingMonitored(keys: "MyFirstKey", "MySecondKey", ...)

Add/Remove Keys for Background Monitoring (Array Option)

Zephyr.addKeysToBeMonitored(keys: ["MyFirstKey", "MySecondKey"])
Zephyr.removeKeysFromBeingMonitored(keys: ["MyFirstKey", "MySecondKey"])

Toggle Automatic Calling of NSUbiquitousKeyValueStore's Synchronization method

Zephyr.syncUbiquitousKeyValueStoreOnChange = true // Default
Zephyr.syncUbiquitousKeyValueStoreOnChange = false // Turns off instantaneous synchronization

Debug Logging

Zephyr.debugEnabled = true // Must be called before sync(_:)
Zephyr.sync()

Sample App

Please ignore the Sample App as I did not add any demo code in the Sample App. It's only in this repo to add support for Carthage.

Author: ArtSabintsev
Source Code: https://github.com/ArtSabintsev/Zephyr
License: MIT license

#ios #swift 

Zephyr: Effortlessly Synchronize UserDefaults over iCloud
 iOS App Dev

iOS App Dev

1656836400

Kotoba | Quickly Search The Built-in Dictionary for IOS

Kotoba

Quickly search the built-in iOS dictionary to see definitions of words. Collect words you want to remember.

Installation (iPhone or iPad)

Note: An Apple Developer account is required.

  1. Clone or download Kotoba from GitHub.
  2. Open code/Kotoba.xcodeproj in Xcode.
  3. Select the Kotoba project file in Navigator, select the "Kotoba" target, then select the "Signing & Capabilities" tab.
  4. Change the "Team" to your Apple Developer account team.
  5. Change the "Bundle Identifier" to com.yourdomain.Kotoba.
  6. Change the "App Groups" to groups.com.yourdomain.Kotoba by adding yours, and deleting the current one.
  7. Select the "ShareExtension" target, and repeat the three steps above.
  8. Open the "Devices and Simulators" window (Shift-Cmd-2) and confirm your device is connected. If not, connect it via USB.
  9. Product > Run.

How to Use

  1. Tap "Add a new word".
  2. Type or dictate the word you want to look up, and hit the Search key.
  3. The iOS system dictionary view will slide up, showing you the definition of the word.
  4. If no definition appears, you may not have the right dictionaries installed. Tap "Manage" in the bottom-left of the screen, and download the appropriate dictionaries.
  5. Tap Done, and you're back at the main screen. The word you just looked up is added to the word history list.
  6. You can delete words from the list by swiping left and tapping "Delete".

Why Kotoba?

The original idea came from @gruber in a DM to @DFstyleguide:

Ever find a good iPhone dictionary app?

Best I’ve found is Terminology, but it’s so complicated I usually just fire up Vesper and use the system “Define” service.

The system one is good; and it’s perfect for when I’m reading in an app. I find it fiddly to use though when I’m reading a printed book.

I want:

  1. Open dictionary app.
  2. Start typing word to look up.
  3. Read definition.

Instead I have to:

  1. Open Vesper
  2. Open a note
  3. Type the word I’m looking for
  4. Select the word, tap Define
  5. Read definition.
  6. Close dictionary and delete the word from my note.

Development Status

Kotoba is in a bare-bones state, with just enough functionality to be useful.

As far as I know, it works fine. But if you find any bugs, or have suggestions for improvement, please raise an issue on GitHub. Any feedback is heartily welcome.

Why Open-Source?

Look, ladies and gents, you are much smarter than I. I am a working developer, but Swift/iOS is not my day job. I made this open-source for two reasons:

  • Honestly, I suspected Kotoba wouldn't be approved for the App Store. If it's open-source, the Xcode-savvy can at least install it on their personal devices.
  • If smarter, more experienced Swift developers can suggest improvements or fix my bugs, I will learn from them.

Which is a nice segue to... how to contribute

Download Details:
Author: willhains
Source Code: https://github.com/willhains/Kotoba
License: MIT license

#swift #ios

Kotoba | Quickly Search The Built-in Dictionary for IOS
 iOS App Dev

iOS App Dev

1656829080

Go Flash Cards for IOS and WatchOS Written in Swift

Go Flashcards for iOS and WatchOS is an application that allows users to create stacks of flashcards and review them on their iPhone, iPad, and Apple Watch. Supports CloudKit Syncing.

Localized for: English, French, German, Japanese, Korean, Simplified Chinese, Spanish

Build Instructions

Download Xcode

At the moment GoFlashcards for iOS and WatchOS requires Swift 4.0 and Xcode 9.2 or newer. Previous versions of Xcode can be downloaded from Apple.

Third party tools

Carthage

Go Flashcards for iOS and WatchOS uses Carthage to manage third party libraries.
To install and configure the third party libraries just run the following in the command line:

carthage update --platform ios

Resources

To report an issue

https://github.com/roymckenzie/Go-Flashcards/issues

Source Code

GitHub: https://github.com/roymckenzie/Go-Flashcards/

Download Details:
Author: roymckenzie
Source Code: https://github.com/roymckenzie/Go-Flashcards
License: GPL-2.0 license

#swift #ios

Go Flash Cards for IOS and WatchOS Written in Swift