• Stars
    star
    270
  • Rank 152,189 (Top 3 %)
  • Language
    Swift
  • License
    MIT License
  • Created over 9 years ago
  • Updated about 1 year ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A Swift package for rapid development using a collection of micro utility extensions for Standard Library, Foundation, and other native frameworks.

ZamzamKit

Build Status Platform Swift Xcode SPM MIT

ZamzamKit is a Swift package for rapid development using a collection of micro utility extensions for Standard Library, Foundation, and other native frameworks.

Installation

Swift Package Manager

.package(url: "[email protected]:ZamzamInc/ZamzamKit.git", .upToNextMajor(from: "5.1.0"))

The ZamzamKit package contains four different products you can import. Add any combination of these to your target's dependencies within your Package.swift manifest:

.target(
    name: "MyAppExample",
    dependencies: [
        .product(name: "ZamzamCore", package: "ZamzamKit"),
        .product(name: "ZamzamLocation", package: "ZamzamKit"),
        .product(name: "ZamzamNotification", package: "ZamzamKit"),
        .product(name: "ZamzamUI", package: "ZamzamKit"),
    ]
)

Note: This library is highly volatile and changes often to stay ahead of cutting-edge technologies. It is recommended to copy over code that you want into your own libraries or fork it.

ZamzamCore

Standard+

Collection

Get distinct elements from an array:

[1, 1, 3, 3, 5, 5, 7, 9, 9].distinct // [1, 3, 5, 7, 9]

Remove an element from an array by the value:

var array = ["a", "b", "c", "d", "e"]
array.remove("c")
array // ["a", "b", "d", "e"]

Easily get the array version of an array slice:

["a", "b", "c", "d", "e"].prefix(3).array

Safely retrieve an element at the given index if it exists:

// Before
if let items = tabBarController.tabBar.items, items.count > 4 {
    items[3].selectedImage = UIImage("my-image")
}
// After
tabBarController.tabBar.items?[safe: 3]?.selectedImage = UIImage("my-image")

[1, 3, 5, 7, 9][safe: 1] // Optional(3)
[1, 3, 5, 7, 9][safe: 12] // nil

Determine if a value is contained within the array of equatable values:

"b".within(["a", "b", "c"]) // true

let status: OrderStatus = .cancelled
status.within([.requested, .accepted, .inProgress]) // false
Dictionary

Convert to JSON string or data:

// Before
guard let data = try? JSONSerialization.data(withJSONObject: merged, options: []),
    let log = String(data: data, encoding: .utf8) else {
        return
}

// After
guard let log = merged.jsonString else {
    return
}
Number

Round doubles, floats, or any floating-point type:

123.12312421.rounded(toPlaces: 3) // 123.123
Double.pi.rounded(toPlaces: 2) // 3.14
String

Create a new random string of given length:

String(random: 10) // "zXWG4hSgL9"
String(random: 4, prefix: "PIN-") // "PIN-uSjm"

Safely use subscript indexes and ranges on strings:

let value = "Abcdef123456"
value[3] // "d"
value[3..<6] // "def"
value[3...6] // "def1"
value[3...] // "def123456"
value[3...99] // nil
value[99] // nil

Validate string against common formats:

"[email protected]".isEmail // true
"123456789".isNumber // true
"zamzam".isAlpha // true
"zamzam123".isAlphaNumeric // true

Remove spaces or new lines from both ends:

" Abcdef123456 \n\r  ".trimmed // "Abcdef123456"

Truncate to a given number of characters:

"Abcdef123456".truncated(3) // "Abc..."
"Abcdef123456".truncated(6, trailing: "***") // "Abcdef***"

Determine if a given value is contained:

"1234567890".contains("567") // true
"abc123xyz".contains("ghi") // false

Injects a separator every nth characters:

"1234567890".separated(every: 2, with: "-") // "12-34-56-78-90"

Remove the characters contained in a given set:

let string = """
    { 0         1
    2                  34
    56       7             8
    9
    }
    """

string.strippingCharacters(in: .whitespacesAndNewlines) // {0123456789}

Replace the characters contained in a given character set with another string:

let set = CharacterSet.alphanumerics
    .insert(charactersIn: "_")
    .inverted

let string = """
    _abcdefghijklmnopqrstuvwxyz
    ABCDEFGHIJKLMNOPQRSTUVWXYZ
    0{1 2<3>4@5#6`7~8?9,0

    1
    """

string.replacingCharacters(in: set, with: "_") //_abcdefghijklmnopqrstuvwxyz_ABCDEFGHIJKLMNOPQRSTUVWXYZ_0_1_2_3_4_5_6_7_8_9_0__1

Match using a regular expression pattern:

"1234567890".match(regex: "^[0-9]+?$") // true
"abc123xyz".match(regex: "^[A-Za-z]+$") // false

Replace occurrences of a regular expression pattern:

"aa1bb22cc3d888d4ee5".replacing(regex: "\\d", with: "*") // "aa*bb**cc*d***d*ee*"

Remove HTML for plain text:

"<p>This is <em>web</em> content with a <a href=\"http://example.com\">link</a>.</p>".htmlStripped // "This is web content with a link."

Encoders and decoders:

value.urlEncoded()
value.urlDecoded()
value.htmlDecoded()
value.base64Encoded()
value.base64Decoded()
value.base64URLEncoded()

Get an encrypted version of the string in hex format:

"[email protected]".sha256() // 973dfe463ec85785f5f95af5ba3906eedb2d931c24e69824a89ea65dba4e813b

Easily get the string version of substring:

"hello world".prefix(5).string

Determine if an optional string is nil or has no characters

var value: String? = "test 123"
value.isNilOrEmpty

Foundation+

Bundle

Get the string from a file within any bundle:

Bundle.main.string(file: "Test.txt") // "This is a test. Abc 123.\n"

Get a generic array from a property list file within any bundle:

let values: [String] = Bundle.main.array(plist: "Array.plist")

values[0] // "Abc"
values[1] // "Def"
values[2] // "Ghi"

Image of BundleArray

let values: [[String: Any]] = Bundle.main.array(plist: "Things.plist")

values[0]["id"] as? Int // 1
values[0]["name"] as? String // "Test 1"
values[0]["description"] as? String // "This is a test for 1.")

values[1]["id"] as? Int // 2)
values[1]["name"] as? String // "Test 2")
values[1]["description"] as? String // "This is a test for 2.")

values[2]["id"] as? Int // 3)
values[2]["name"] as? String // "Test 3")
values[2]["description"] as? String // "This is a test for 3.")

Image of BundleArray

Get a dictionary from a property list file within any bundle:

let values: [String: Any] = Bundle.main.contents(plist: "Settings.plist")

values["MyString1"] as? String // "My string value 1."
values["MyNumber1"] as? Int // 123
values["MyBool1"] as? Bool // false
values["MyDate1"] as? Date // 2018-11-21 15:40:03 +0000

Image of BundleDictionary

Color

Additional color initializers:

UIColor(hex: 0x990000)
UIColor(hex: 0x4286F4)
UIColor(rgb: (66, 134, 244))
UIColor.random
Currency

A formatter that converts between numeric values and their textual currency representations:

let formatter = CurrencyFormatter()
formatter.string(fromAmount: 123456789.987) // "$123,456,789.99"

let formatter2 = CurrencyFormatter(for: Locale(identifier: "fr-FR"))
formatter2.string(fromCents: 123456789) // "1 234 567,89 €"
Data

Get a hex string representation of the data:

Data()?.hexString() // 68626a4a424a6a68626a68616420663773376474663720737567796f3837545e49542a69797567

Get an encrypted version of the data:

Data()?.sha256()
Date

Determine if a date is in the past or future:

Date(timeIntervalSinceNow: -100).isPast // true
Date(timeIntervalSinceNow: 100).isPast // false

Date(timeIntervalSinceNow: 100).isFuture // true
Date(timeIntervalSinceNow: -100).isFuture // false

Determine if a date is today, yesterday, or tomorrow:

Date().isToday // true
Date(timeIntervalSinceNow: -90_000).isYesterday // true
Date(timeIntervalSinceNow: 90_000).isTomorrow // true

Determine if a date is within a weekday or weekend period:

Date().isWeekday // false
Date().isWeekend // true

Get the beginning or end of the day:

Date().startOfDay // "2018/11/21 00:00:00"
Date().endOfDay // "2018/11/21 23:59:59"

Get the beginning or end of the month:

Date().startOfMonth // "2018/11/01 00:00:00"
Date().endOfMonth // "2018/11/30 23:59:59"

Determine if a date is current:

let date = Date(fromString: "2018/03/22 09:40")
date.isCurrentWeek
date.isCurrentMonth
date.isCurrentYear

Determine if a date is between two other dates:

let date = Date()
let date1 = Date(timeIntervalSinceNow: 1000)
let date2 = Date(timeIntervalSinceNow: -1000)

date.isBetween(date1, date2) // true

Determine if a date is beyond a specified time window:

let date = Date(fromString: "2018/03/22 09:40")
let fromDate = Date(fromString: "2018/03/22 09:30")

date.isBeyond(fromDate, bySeconds: 300) // true
date.isBeyond(fromDate, bySeconds: 1200) // false

Use specific calendar for data manipulations:

let date = Date(fromString: "2018/03/22 09:40")
let calendar = Calendar(identifier: .chinese)

date.isToday(using: calendar)
date.isWeekday(using: calendar)
date.isCurrentMonth(using: calendar)
date.isToday(using: calendar)
date.startOfDay(using: calendar)
date.startOfMonth(using: calendar)

Determine if a date is beyond a specified time window:

let date = Date(fromString: "2018/03/22 09:40")
let fromDate = Date(fromString: "2018/03/22 09:30")

date.isBeyond(fromDate, bySeconds: 300) // true
date.isBeyond(fromDate, bySeconds: 1200) // false

Create a date from a string:

Date(fromString: "2018/11/01 18:15")
Date(fromString: "1440/03/01 18:31", calendar: Calendar(identifier: .islamic))

Format a date to a string:

Date().string(format: "MMM d, h:mm a") // "Jan 3, 8:43 PM"
Date().string(style: .full, calendar: Calendar(identifier: .hebrew)) // "Friday, 1 Kislev 5779"
Date().string(formatter: .MM_dd_yyyy_HH_mm)

Format a time interval to display as a timer.

let date = Date(fromString: "2016/03/22 09:45")
let fromDate = Date(fromString: "2016/03/22 09:40")

date.timerString(from: fromDate)

// Prints "00:05:00"

Get the decimal representation of the time:

Date(fromString: "2018/10/23 18:15").timeToDecimal // 18.25

Increment years, months, days, hours, or minutes:

let date = Date()
date + .years(1)
date + .months(2)
date - .days(4)
date - .hours(6)
date + .minutes(12)
date + .days(5, Calendar(identifier: .chinese))

Convert between time interval units:

let diff = date.timeIntervalSince(date2) // 172,800 seconds
diff.minutes // 2,800 minutes
diff.hours // 48 hours
diff.days // 2 days

Time zone context and offset:

let timeZone = TimeZone(identifier: "Europe/Paris")
timeZone?.isCurrent // false
timeZone?.offsetFromCurrent // -21600

Normalize date calculations and data storage:

let timeZone: TimeZone = .posix // GMT
let locale: Locale = .posix // en_US_POSIX
Decodable

Get a value of the type you specify, decoded from a JSON string.

let jsonString = "{\"test1\":29,\"test2\":62,\"test3\":33,\"test4\":24,\"test5\":14,\"test6\":72}"
let jsonObject: [String: Int] = jsonString.decode()

// Result
[
    "test1": 29,
    "test2": 62,
    "test3": 33,
    "test4": 24,
    "test5": 14,
    "test6": 72
]

Get a type-erased Decodable value:

let json = """
{
    "boolean": true,
    "integer": 1,
    "double": 3.14159265358979323846,
    "string": "Abc123",
    "date": "2018-12-05T15:28:25+00:00",
    "array": [1, 2, 3],
    "nested": {
        "a": "alpha",
        "b": "bravo",
        "c": "charlie"
    }
}
""".data(using: .utf8)

let decoder = JSONDecoder()
let dictionary = try decoder.decode([String: AnyDecodable].self, from: json)

dictionary["boolean"].value // true
dictionary["integer"].value // 1
dictionary["string"].value // Abc123

Skip failed elements during decoding instead exiting collection completely; lossy array decoding.

init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    
    self.authors = try container.decode(FailableCodableArray<Author>.self, forKey: .author)
}
DispatchQueue

Provides configured queues for executing commonly related work items:

DispatchQueue.database.async {
    // Database work here
}

DispatchQueue.transform.async {
    // Parse or decode work here
}

DispatchQueue.logger.async {
    // Logging work here
}
FileManager

Get URL or file system path for a file:

FileManager.default.url(of: fileName, from: .documentDirectory)
FileManager.default.path(of: fileName, from: .cachesDirectory)

Get URL or file system paths of files within a directory:

FileManager.default.urls(from: .documentDirectory)
FileManager.default.paths(from: .downloadsDirectory)

Retrieve a file remotely and persist to local disk:

FileManager.default.download(from: "http://example.com/test.pdf") { url, response, error in
    // The `url` parameter represents location on local disk where remote file was downloaded.
}
Location

Get the location details for coordinates:

CLLocation(latitude: 43.6532, longitude: -79.3832).geocoder { meta in
    print(meta.locality)
    print(meta.country)
    print(meta.countryCode)
    print(meta.timezone)
    print(meta.administrativeArea)
}

Get the closest or farthest location from a list of coordinates:

let coordinates = [
    CLLocationCoordinate2D(latitude: 43.6532, longitude: -79.3832),
    CLLocationCoordinate2D(latitude: 59.9094, longitude: 10.7349),
    CLLocationCoordinate2D(latitude: 35.7750, longitude: -78.6336),
    CLLocationCoordinate2D(latitude: 33.720817, longitude: 73.090032)
]

coordinates.closest(to: homeCoordinate)
coordinates.farthest(from: homeCoordinate)

Determine if location services is enabled and authorized for always or when in use:

CLLocationManager.isAuthorized // bool
URL

Append a query string parameter to a URL:

let url = URL(string: "https://example.com?abc=123&lmn=tuv&xyz=987")
url?.appendingQueryItem("def", value: "456") // "https://example.com?abc=123&lmn=tuv&xyz=987&def=456"
url?.appendingQueryItem("xyz", value: "999") // "https://example.com?abc=123&lmn=tuv&xyz=999"

Append a dictionary of query string parameters to a URL:

let url = URL(string: "https://example.com?abc=123&lmn=tuv&xyz=987")
url?.appendingQueryItems([
    "def": "456",
    "jkl": "777",
    "abc": "333",
    "lmn": nil
]) // "https://example.com?xyz=987&def=456&abc=333&jkl=777"

Remove a query string parameter to a URL:

let url = URL(string: "https://example.com?abc=123&lmn=tuv&xyz=987")
url?.removeQueryItem("xyz") // "https://example.com?abc=123&lmn=tuv"

Query a URL from a parameter name:

let url = URL(string: "https://example.com?abc=123&lmn=tuv&xyz=987")
url?.queryItem("aBc") // "123"
url?.queryItem("lmn") // "tuv"
url?.queryItem("yyy") // nil
URLSession

A thin wrapper around URLSession and URLRequest for simple network requests:

let request = URLRequest(
    url: URL(string: "https://httpbin.org/get")!,
    method: .get,
    parameters: [
        "abc": 123,
        "def": "test456",
        "xyz": true
    ],
    headers: [
        "Abc": "test123",
        "Def": "test456",
        "Xyz": "test789"
    ]
)
 
let networkManager = NetworkManager(
    service: NetworkFoundationService()
)

networkManager.send(with: request) { result in
    switch result {
    case let .success(response):
        response.data
        response.headers
        response.statusCode
    case let .failure(error):
        error.statusCode
    }
}

Or call multiple URL requests simultaneously:

let request1 = URLRequest(
    url: URL(string: "https://httpbin.org/get")!,
    method: .get
)

let request2 = URLRequest(
    url: URL(string: "https://httpbin.org/post")!,
    method: .post
)

let request3 = URLRequest(
    url: URL(string: "https://httpbin.org/delete")!,
    method: .delete
)

networkManager.send(requests: request1, request2, request3) { firstResult, anotherResult, otherResult in
    switch firstResult {
    case let .success(response):
        response.data
    case let .failure(error):
        error.statusCode
    }

    switch anotherResult {
    case let .success(response):
        response.data
    case let .failure(error):
        error.statusCode
    }
    
    switch otherResult {
    case let .success(response):
        response.data
    case let .failure(error):
        error.statusCode
    }
}

Use an adapter to intercept any URLRequest and modify for all network requests:

struct CustomURLRequestAdapter: URLRequestAdapter {
        
    func adapt(_ request: URLRequest) -> URLRequest {
        var request = request
        request.setValue("1", forHTTPHeaderField: "X-Test-1")
        request.setValue("2", forHTTPHeaderField: "X-Test-2")
        return request
    }
}

let request = URLRequest(
    url: URL(string: "https://httpbin.org/get")!,
    method: .get
)
 
let networkManager = NetworkManager(
    service: NetworkFoundationService(),
    adapter: CustomURLRequestAdapter()
)

networkManager.send(with: request) { result in
    guard case let .success(response) else { return }

    request.value(forHTTPHeaderField: "X-Test-1") == nil // true
    request.value(forHTTPHeaderField: "X-Test-2") == nil // true

    response.request.value(forHTTPHeaderField: "X-Test-1") == "1" // true
    response.request.value(forHTTPHeaderField: "X-Test-2") == "2" // true
}

Utilities

AppInfo

Get details of the current app:

struct SomeStruct: AppInfo {

}

let someStruct = SomeStruct()

someStruct.appDisplayName // "Zamzam App"
someStruct.appBundleID // "io.zamzam.app"
someStruct.appVersion // "1.0.0"
someStruct.appBuild // "23"
someStruct.isInTestFlight // false
someStruct.isRunningOnSimulator // false
Apply

Set properties with closures just after initializing:

let paragraph = NSMutableParagraphStyle().apply {
    $0.alignment = .center
    $0.lineSpacing = 8
}

let label = UILabel().apply {
    $0.textAlignment = .center
    $0.textColor = UIColor.black
    $0.text = "Hello, World!"
}

UITabBar.appearance().apply {
    $0.barStyle = .dark
    $0.tintColor = .blue
}
Atomic

A thread-safe value that handles concurrent reads and writes (read more):

var temp = Atomic<Int>(0)

DispatchQueue.concurrentPerform(iterations: 1_000_000) { index in
temp.value { $0 += 1 }
}

XCTAssertEqual(temp.value, 1_000_000) // true
AppMigration

Manages blocks of code that only need to run once on version updates in apps:

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?
    let migration = AppMigration()

    func application(_ application: UIApplication, willFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool {
        migration
            .performUpdate {
                print("Migrate update occurred.")
            }
            .perform(forVersion: "1.0") {
                print("Migrate to 1.0 occurred.")
            }
            .perform(forVersion: "1.7") {
                print("Migrate to 1.7 occurred.")
            }
            .perform(forVersion: "2.4") {
                print("Migrate to 2.4 occurred.")
            }
            
        return true
    }
}
BackgroundTask

Easily execute a long-running background task:

BackgroundTask.run(for: application) { task in
    // Perform finite-length task...
    task.end()
}
Keychain

A thin wrapper to manage Keychain, or other services that conform to KeychainService:

let keychain = KeychainManager(
    service: KeychainExternalService()
)

keychain.set("kjn989hi", forKey: .token)

keychain.get(.token) {
    print($0) // "kjn989hi"
}

// Define strongly-typed keys
extension KeychainAPI.Key {
    static let token = KeychainAPI.Key("token")
}
Logger

Create loggers that conform to LogService and add to LogManager (console and os_log are included):

let log = LogManager(
    services: [
        LogServiceConsole(minLevel: .debug),
        LogServiceOS(
            minLevel: .warning,
            subsystem: "io.zamzam.Basem-Emara",
            category: "Application"
        ),
        MyCustomLogger()
    ]
)

log.error("There was an error.")
SystemConfiguration

Determine if the device is connected to a network:

import SystemConfiguration

SCNetworkReachability.isOnline

Infixes

ConditionalAssignment ?=

Assign a value if not nil:

var test: Int? = 123
var value: Int? = nil

test ?= value
// test == 123

value = 456
test ?= value
// test == 456
NilOrEmptyAssignment ??+

Assign a value if not nil or empty:

var test: String
var value: String?

test = value ??+ "Abc"
// test == "Abc"

value = ""
test = value ??+ "Lmn"
// test == "Lmn"

value = "Xyz"
test = value ??+ "Rst"
// test == "Xyz"

ZamzamLocation

LocationManager

Location manager that offers Combine wrappers:

func fetchLocation() {
    log.debug("Begin location authorization...")

    guard locationManager.isAuthorized else {
        locationManager.requestAuthorization()
            .handleEvents(receiveOutput: { [weak self] granted in
                guard granted else {
                    self?.log.error("Location authorization denied")
                    return
                }

                self?.log.debug("Location authorization granted")
            })
            .first { $0 }
            .sink { [weak self] _ in self?.fetchLocation() }
            .store(in: &cancellable)

        return
    }

    log.debug("Begin fetching location...")

    locationManager
        .startUpdatingLocation()
        .retry(3)
        .catch { [weak self] error -> AnyPublisher<CLLocation, Never> in
            self?.log.error("GPS location coordinate failed", error: error)
            return Empty(completeImmediately: true).eraseToAnyPublisher()
        }
        .first()
        .sink { [weak self] in
            self?.log.debug("Location coordinate: \($0)")
            self?.locationManager.stopUpdatingLocation()
            self?.log.debug("Location turned off GPS")
        }
        .store(in: &cancellable)
}

ZamzamNotification

UserNotification

Registers the local and remote notifications with the categories and actions it supports:

UNUserNotificationCenter.current().register(
    delegate: self,
    categories: [
        "order": [
            UNNotificationAction(
                identifier: "confirmAction",
                title: "Confirm",
                options: [.foreground]
            )
        ],
        "chat": [
            UNTextInputNotificationAction(
                identifier: "replyAction",
                title: "Reply",
                options: [],
                textInputButtonTitle: "Send",
                textInputPlaceholder: "Type your message"
            )
        ],
        "offer": nil
    ],
    authorizations: [.alert, .badge, .sound],
    completion: { granted in
        granted
            ? log.debug("Authorization for notification succeeded.")
            : log.warn("Authorization for notification not given.")
    }
)

Get a list of all pending or delivered user notifications:

UNUserNotificationCenter.current().getNotificationRequests { notifications in
    notifications.forEach {
        print($0.identifier)
    }
}

Find the pending or delivered notification request by identifier:

UNUserNotificationCenter.current().get(withIdentifier: "abc123") {
    print($0?.identifier)
}

UNUserNotificationCenter.current().get(withIdentifiers: ["abc123", "xyz789"]) {
    $0.forEach {
        print($0.identifier)
    }
}

Determine if the pending or delivered notification request exists:

UNUserNotificationCenter.current().exists(withIdentifier: "abc123") {
    print("Does notification exist: \($0)")
}

Schedules local notifications for delivery:

UNUserNotificationCenter.current().add(
    body: "This is the body for time interval",
    timeInterval: 5
)

UNUserNotificationCenter.current().add(
    body: "This is the body for time interval",
    title: "This is the snooze title",
    timeInterval: 60,
    identifier: "abc123-main"
)

UNUserNotificationCenter.current().add(
    body: "This is the body for time interval",
    title: "This is the misc1 title",
    timeInterval: 60,
    identifier: "abc123-misc1",
    category: "misc1Category"
)

UNUserNotificationCenter.current().add(
    body: "This is the body for time interval",
    title: "This is the misc2 title",
    timeInterval: 60,
    identifier: "abc123-misc2",
    category: "misc2Category",
    userInfo: [
        "id": post.id,
        "link": post.link,
        "mediaURL": mediaURL
    ],
    completion: { error in
        guard error == nil else { return }
        // Added successfully
    }
)

UNUserNotificationCenter.current().add(
    date: Date(timeIntervalSinceNow: 5),
    body: "This is the body for date",
    repeats: .minute,
    identifier: "abc123-repeat"
)

Get a remote image from the web and convert to a user notification attachment:

UNNotificationAttachment.download(from: urlString) {
    guard case let .success(attachment) = $0 else {
        log.error("Could not download the remote resource (\(urlString)): \($0.error?.debugDescription).")
        return
    }

    UNUserNotificationCenter.current().add(
        body: "This is the body",
        attachments: [attachment]
    )
}

Remove pending or delivered notification requests by identifiers, categories, or all:

UNUserNotificationCenter.current().remove(withIdentifier: "abc123")
UNUserNotificationCenter.current().remove(withIdentifiers: ["abc123", "xyz789"])
UNUserNotificationCenter.current().remove(withCategory: "chat") { /* Done */ }
UNUserNotificationCenter.current().remove(withCategories: ["order", "chat"]) { /* Done */ }
UNUserNotificationCenter.current().removeAll()

ZamzamUI

SwiftUI

Documentation coming soon!

Extensions Modifiers Styles Views

WatchKit

CLKComplicationServer

Invalidates and reloads all timeline data for all complications:

// Before
guard let complications = activeComplications, !complications.isEmpty else { return }
complications.forEach { reloadTimeline(for: $0) }
// After
CLKComplicationServer.sharedInstance().reloadTimelineForComplications()

Extends all timeline data for all complications:

// Before
guard let complications = activeComplications, !complications.isEmpty else { return }
complications.forEach { extendTimeline(for: $0) }
// After
CLKComplicationServer.sharedInstance().extendTimelineForComplications()

Author

License

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

More Repositories

1

SwiftUI-NewsReader

Yet another SwiftUI example
Swift
68
star
2

Shank

A Swift micro-library that provides lightweight dependency injection.
Swift
62
star
3

SwiftyPress

An iOS framework for WordPress integration.
Swift
54
star
4

ThemeKit

Protocol-Oriented Themes for iOS Apps
Swift
32
star
5

BasemEmaraBlogIOS

The iOS app for http://basememara.com.
Swift
25
star
6

PrayKit

Prayer and Qibla library used to power Pray Watch app
Swift
12
star
7

PluggableAppDelegate

Pluggable AppDelegate Services for iOS
Swift
11
star
8

SwiftPrayTimes

Pray Times provides a set of handy functions to calculate prayer times for any location around the world, based on a variety of calculation methods currently used in Muslim communities.
Swift
11
star
9

Swift-Clean-Architecture

Clean Architecture for iOS
Swift
9
star
10

SynchronizedGeneric

Creating Thread-Safe Generic Values in Swift
Swift
9
star
11

ExtendedTableCollection

Protocol-Oriented TableView and CollectionView: http://basememara.com/protocol-oriented-tableview-collectionview
Swift
9
star
12

SwiftyRouter

Protocol-Oriented Router in Swift: https://basememara.com/protocol-oriented-router-in-swift/
Swift
5
star
13

SwiftyPress-WP

WordPress plugin for the Swifty Press framework: https://github.com/ZamzamInc/SwiftyPress
PHP
5
star
14

apple-watchkit-number-keypad

Creating a number keypad for Apple Watch / WatchKit.
Swift
5
star
15

SwiftyLocations

A closure-based wrapper for CLLocationManager to provide observable locations, instead of using delegates: https://basememara.com/swifty-locations-observables
Swift
5
star
16

QiblaDirection

Detects user's angle to Kaaba
Swift
3
star
17

SwiftyLocalizable

https://basememara.com/swifty-localization-xcode-support
Swift
2
star
18

Pure-Swift-DI

A Swifty Protocol-Oriented Dependency Injection
Swift
2
star
19

ramadan-countdown

An app that counts down to the next Ramadan with training tips and notifications along the way. The training intensifies as Ramadan comes nearer. Submitted to the masjid.io hackathon 2015.
JavaScript
2
star
20

iOS-Password-Autofill-ASWebAuthenticationSession

Swift
1
star
21

polymer-kendoui

Web Components (via the Polymer Project) for Kendo UI Widgets
JavaScript
1
star
22

swift-strong-reference-cycle-sample

Code to illustrate strong reference cycle and for using with Xcode Instruments
Swift
1
star
23

basem-mono

Basem Emara
TypeScript
1
star
24

BasemEmaraBlogAndroid

The Android app for http://basememara.com.
Java
1
star